Este documento discute testes unitários e de integração no desenvolvimento de software. Ele explica que testes unitários testam unidades individuais de código de forma isolada, enquanto testes de integração verificam a integração entre classes e componentes. Além disso, o documento descreve a abordagem de desenvolvimento outside-in guiada por testes, na qual se começa com testes de aceitação de alto nível e se implementa funcionalidades iterativamente com TDD para atender esses testes.
Apresentação intitulada "Rumo à Certificação PHP" na V Jornada Acadêmica de Análise e Desenvolvimento de Sistemas do IFRS - Câmpus Bento Gonçalves (que ocorreu entre os dias 6 e 7/11/2014):
http://ads.bento.ifrs.edu.br/noticias/view/id/118/
Apresentei as vantagens em ser um profissional certificado, como funciona a compra do voucher, formato do teste, qual o conteúdo do exame, tecnologias relacionadas que também são abordadas, estratégias para conduzir melhor a prova, exemplos de questões, como as versões do PHP influenciam no teste, diferenças da ZCE (PHP 5.3) x ZCPE (PHP 5.5), armadilhas, técnicas de resolução das perguntas, como lidar com as inconsistências da linguagem, preparatório, simulados e a lista "Rumo à Certificação PHP":
http://www.rumoacertificacaophp.com
Palestra TDC SP 2018: Nesta palestra, vamos apresentar o Flutter, o SDK de desenvolvimento multiplataforma do Google para criação de apps com performance de app nativo e visual atrativo, além de mostrar, na prática, os aplicativos gerados para as duas plataformas a partir de um único código fonte. Falaremos ainda da experiência de um trabalho conjunto de desenvolvedores Android e iOS na criação de um app que foi destaque no Google I/O.
Palestra TDC SP 2018 / atualização InovaBRA: Nesta palestra, vamos apresentar o Flutter, o SDK de desenvolvimento multiplataforma do Google para criação de apps com performance de app nativo e visual atrativo, além de mostrar, na prática, os aplicativos gerados para as duas plataformas a partir de um único código fonte. Falaremos ainda da experiência de um trabalho conjunto de desenvolvedores Android e iOS na criação de um app que foi destaque no Google I/O.
Apresentação intitulada "Rumo à Certificação PHP" na V Jornada Acadêmica de Análise e Desenvolvimento de Sistemas do IFRS - Câmpus Bento Gonçalves (que ocorreu entre os dias 6 e 7/11/2014):
http://ads.bento.ifrs.edu.br/noticias/view/id/118/
Apresentei as vantagens em ser um profissional certificado, como funciona a compra do voucher, formato do teste, qual o conteúdo do exame, tecnologias relacionadas que também são abordadas, estratégias para conduzir melhor a prova, exemplos de questões, como as versões do PHP influenciam no teste, diferenças da ZCE (PHP 5.3) x ZCPE (PHP 5.5), armadilhas, técnicas de resolução das perguntas, como lidar com as inconsistências da linguagem, preparatório, simulados e a lista "Rumo à Certificação PHP":
http://www.rumoacertificacaophp.com
Palestra TDC SP 2018: Nesta palestra, vamos apresentar o Flutter, o SDK de desenvolvimento multiplataforma do Google para criação de apps com performance de app nativo e visual atrativo, além de mostrar, na prática, os aplicativos gerados para as duas plataformas a partir de um único código fonte. Falaremos ainda da experiência de um trabalho conjunto de desenvolvedores Android e iOS na criação de um app que foi destaque no Google I/O.
Palestra TDC SP 2018 / atualização InovaBRA: Nesta palestra, vamos apresentar o Flutter, o SDK de desenvolvimento multiplataforma do Google para criação de apps com performance de app nativo e visual atrativo, além de mostrar, na prática, os aplicativos gerados para as duas plataformas a partir de um único código fonte. Falaremos ainda da experiência de um trabalho conjunto de desenvolvedores Android e iOS na criação de um app que foi destaque no Google I/O.
Jornal Daqui: notícias da Lagoinha e regiãolorenatarcia
Jornal Daqui é um projeto do laboratório de Convergência de Mídias e da TV Universitária do Centro Universitário de Belo Horizonte.
Jornalismo hiperlocal, convergente e participativo, com notícias do bairro Lagoinha e região.
Coordenação: profa. Lorena Tárcia
Acompanhe: www.jornaldaquitvuni.webnode.com.br
www.unibh.br/tvuni
Design de código: princípios e práticas para ter um código sustentávelAndrews Medina
Escrever código é algo fácil o difícil é manter seu código limpo, legível por muito tempo sem transformá-lo em um código legado. Irei abordar várias práticas, princípios e padrões que nos ajudam a escrever e manter o código limpo.
Jornal Daqui: notícias da Lagoinha e regiãolorenatarcia
Jornal Daqui é um projeto do laboratório de Convergência de Mídias e da TV Universitária do Centro Universitário de Belo Horizonte.
Jornalismo hiperlocal, convergente e participativo, com notícias do bairro Lagoinha e região.
Coordenação: profa. Lorena Tárcia
Acompanhe: www.jornaldaquitvuni.webnode.com.br
www.unibh.br/tvuni
Design de código: princípios e práticas para ter um código sustentávelAndrews Medina
Escrever código é algo fácil o difícil é manter seu código limpo, legível por muito tempo sem transformá-lo em um código legado. Irei abordar várias práticas, princípios e padrões que nos ajudam a escrever e manter o código limpo.
TDC2016 Boas Práticas SQL em Banco Relacional para DesenvolvedoresFernando Franquini
Com o grande crescimento da utilização dos Frameworks, muitos Desenvolvedores deixam para que esses resolvam todos os 'problemas' referentes a SQL, mas temos muitos casos que nada melhor que um SQL direto no banco de dados para resolver seu problema da melhor forma. O objetivo dessa palestra é mostrar algumas boas práticas aos desenvolvedores na utilização de SQL que possam aumentar sua produtividade e assertividade no dia a dia.
Como usar a biblioteca SimpleTest do PHP para ganhar tempo resolvendo os problemas mas simples do dia-a-dia. Essa apresentação foi elaborada para o PHPConference2008 que aconteceu em Osasco, SP Brasil.
Minicurso de Ruby on Rails que ocorreu de 25/11/2014 até 28/11/2014 no Instituto Federal de Educação Ciência e Tecnologia do Sudeste de Minas Campus Barbacena
In my last six years working at Globo.com I made many mistakes working with A/B experimentation. In this presentation I cataloged some of those mistakes as advice to avoid made them again in the future.
Machine Learning e experimentos online para evitar o cancelamento no GloboPlayTiago Albineli Motta
Palestra ministrada na Qcon SP 2018
Vamos explorar as técnicas de Machine Learning utilizadas para reduzir o churn do GloboPlay principalmente para encontrar os assinantes mais prováveis de cancelar e as métricas correlacionadas, especialmente no uso de técnicas de clusterização e classificação em dados desbalanceados.
Também falaremos sobre algoritmos de recomendação e intervenções experimentadas através de testes AB simultâneos e como evitar a interferência de um experimento em outro. Ao final da palestra o participante terá uma clara noção da importância, quais técnicas e como utilizar ciência de dados para atingir resultados tangíveis em um produto.
Palestra sobre meta-programação em python ministrada no TDC 2012 e na SEMCOMP 2012. Corrigido o verbo "estender" que estava antes "extender" e colocado realce de sintaxe.
Slides da palestra sobre Redis apresentada no Dev in Santos. Modifiquei apenas o "classe A" para "sensacional" e corrigi o código do slide "Utilizando sets".
Palestra também apresentada no NoSqlBahia e no TDC 2012
4. Testes unitários
Testando a menor unidade de código
possível da forma mais isolada possível
5. Unitários: Exemplo inicial
describe Jogador do
let(:objetivo) { double }
subject { Jogador.new(objetivo) }
describe '#venceu?' do
it "deveria ser true se completou objetivo" do
objetivo.stub!(:completo?) { true }
subject.venceu?.should be
end
end
end
6. Unitários: Código testado
class Jogador
def initialize(objetivo)
@objetivo = objetivo
end
def venceu?
@objetivo.completo?
end
end
7. Unitários: Exemplo + completo
describe Jogador do
let(:objetivo) { double }
let(:partida) { double }
subject { Jogador.new(partida, objetivo) }
describe '#venceu?' do
#...
it "deveria informar partida e jogador ao objetivo" do
objetivo.should_receive(:completo?).with( subject, partida )
subject.venceu?
end
end
end
8. Unitários: Código testado
class Jogador
def initialize(partida, objetivo)
@objetivo = objetivo
@partida = partida
end
def venceu?
@objetivo.completo? self, @partida
end
end
10. Unitários: Prós
Rápido de fazer e executar
Incentiva o baixo acoplamento
Facilita resolução de algoritmos
11. Unitários: Contras
Falsa sensação de terminado
Insegurança ao trocar contratos
12. Unitários: Exemplo de erro
class Objetivo
def completo?(jogador, partida)
def terminado?(jogador, partida)
#...
end
end
13. Testes de integração
Testando para garantir que as classes
e componentes estejam se integrando
corretamente
14. Integração: Exemplo
describe Jogador, "com objetivo de conquistar 24 territorios" do
let(:objetivo) { Objetivos::Conquistar24Territorios }
let(:partida) { Partida.new }
subject { Jogador.create(partida: partida, objetivo: objetivo) }
it "deveria vencer se tiver 24 territorios" do
24.times { subject.territorios << Territorio.new }
subject.venceu?.should be
end
end
15. Integração: Modulo de Objetivos
module Objetivos
class Conquistar24Territorios
def self.completo?(jogador, partida)
jogador.territorios.count >= 24
end
end
end
16. Integração: End to end
Comportamento do ponto de vista do usuário
Exemplo:
Dado que o jogador tem 23 territórios
E o objetivo dele é conquistar 24 territórios
Quando o jogador conquistar 1 território
E finalizar a rodada
Então Jogador vence a partida
17. Integração: Prós
Garante o funcionamento do sistema
Sensação de tarefa concluída
18. Integração: Contras
Testes mais lentos
Dificuldade de entender origem de erros
19. Como unir?
Sensação de finalizado e segurança dos testes
de integração end to end
Facilidade, rapidez e desacoplamento
proporcionados pelos testes unitários
20. Desenvolvimento Outside-in
Definir caso de aceitação
Preparar teste end-to-end
Desenvolver funcionalidade com TDD
Repetir isso infinitamente
21. Outside-in: Caso de aceitação
Jogador com 4 exércitos em um território
ataca território vizinho que possui apenas
1 exército e o conquista
22. Setup – inicialmente fake
feature "Atacar" do
scenario "territorio vizinho com 3 dados e conquistar" do
dado_jogador_com_exercitos_no_pais(4, :brasil)
dado_jogador_com_exercitos_no_pais(1, :argentina)
end
private
def dado_jogador_com_exercitos_no_pais(exercitos, pais)
end
end
23. Acesso - falhando
feature "Atacar" do
before :each do
@partida = Partida.create
end
scenario "territorio vizinho com 3 dados e conquistar" do
jogador1 = dado_jogador_com_exercitos_no_pais(4, :brasil)
jogador2 = dado_jogador_com_exercitos_no_pais(1, :argentina)
dado_que_jogador_esta_logado(jogador1)
end
#...
def dado_que_jogador_esta_logado(jogador)
visit partida_path(@partida)
end
end
24. Acesso - funcionando
models/partida.rb:
class Partida < ActiveRecord::Base
end
config/routes.rb:
War::Application.routes.draw do
resources :partidas
end
controllers/partida_controller.rb:
class PartidasController < ApplicationController
def show
end
end
Partidas/show.html.erb:
<h1>Ok</h1>
25. Primeira interação - falhando
scenario "territorio vizinho com 3 dados e conquistar" do
#...
dado_que_jogador_esta_logado(jogador1)
quando_selecionar_territorio_que_vai_atacar(:brasil)
end
private
#...
def quando_selecionar_territorio_que_vai_atacar(pais)
click_link pais.to_s
end
26. Primeira interação - funcionando
views/partidas.html.erb:
<a href="#">brasil</a>
27. Mais interações - falhando
scenario "territorio vizinho com 3 dados e conquistar" do
#...
quando_selecionar_territorio_que_vai_atacar(:brasil)
quando_selecionar_territorio_atacado(:argentina)
quando_confirmar_ataque
end
#...
def quando_selecionar_territorio_atacado(pais)
click_link pais.to_s
end
def quando_confirmar_ataque
click_button 'Atacar'
end
29. Verficação - falhando
scenario "territorio vizinho com 3 dados e conquistar" do
#...
quando_confirmar_ataque
entao_territorio_eh_conquistado(:argentina)
end
#...
def entao_territorio_eh_conquistado(pais)
find('#mensagem').text.strip
.should == "Territorio '#{pais}' conquistado"
end
33. Teste unitário do controller
describe AtaquesController, 'POST' do
it 'deveria redirecionar para a partida' do
post :create, partida_id: 1, ataque: {}
response.should redirect_to(partida_path(1))
end
it 'deveria colocar mensagem de sucesso' do
post :create, partida_id: 1, ataque: { pais_atacado: 'argentina' }
flash[:mensagem].
should == "Territorio 'argentina' conquistado"
end
end
34. Controller de ataque
class AtaquesController < ApplicationController
def create
pais = params[:ataque][:pais_atacado]
flash[:mensagem] = "Territorio #{pais}' conquistado"
redirect_to partida_path(params[:partida_id])
end
end
35. View tem que mudar
<div id="mensagem"><%= flash[:mensagem] %></div>
<%= form_for :ataque,
url: partida_ataques_url(@partida),
method: :post do |f| %>
<a href="#">brasil</a>
<a href="#">argentina</a>
<%= f.hidden_field 'pais_que_ataca' %>
<%= f.hidden_field 'pais_atacado' %>
<%= f.submit value: 'Atacar' %>
<% end %>
36. Javascript incluído
var paisQueAtaca = $('#ataque_pais_que_ataca');
var paisAtacado = $('#ataque_pais_atacado');
var paisAtual = paisQueAtaca;
var marcar = function(texto) {
paisAtual.val(texto);
}
var trocaPaisAtual = function() {
paisAtual = paisAtual == paisQueAtaca ?
paisAtacado : paisQueAtaca;
}
$('a').click(function(e){
e.preventDefault();
marcar($(this).text());
trocaPaisAtual();
});
37. O que temos até agora? (2)
Teste View Controller Model
38. Teste unitário do controller
describe AtaquesController, 'POST' do
let(:partida) { double(id: 1, executar_ataque: true) }
before :each do
Partida.stub!(:find) { partida }
end
#...
it 'deveria executar ataque na partida' do
params_ataque = {"meu_ataque" => true}
partida.should_receive(:executar_ataque).with(params_ataque)
post :create, partida_id: 1, ataque: params_ataque
end
end
39. Controller de ataque
class AtaquesController < ApplicationController
def create
ataque = params[:ataque]
partida = Partida.find params[:partida_id]
partida.executar_ataque(ataque)
pais = ataque[:pais_atacado]
flash[:mensagem] = "Territorio '#{pais}' conquistado"
redirect_to partida_path(partida)
end
end
40. Teste end-to-end falha
$ rake spec:acceptance
Failures:
1) Atacar territorio vizinho com 3 dados e conquistar
Failure/Error: find('#mensagem').text.strip.should == "Territorio...
Capybara::ElementNotFound:
Unable to find css "#mensagem"
Erro dificil de encontrar a origem!
41. Método não encontrado
$ tail -f log/test.log
Completed 500 Internal Server Error in 3ms
undefined method `executar_ataque' for #<Partida:0xa5549c0>
class Partida < ActiveRecord::Base
def executar_ataque(attrs)
end
end
42. Um pouco de ousadia
Que tal começar um outro caso
de aceitação antes de terminar
este?
43. Caso da derrota
scenario "territorio vizinho com 3 dados e conquistar", js: true do
dado_que_dados_vermelhos_estao_sortudos
#...
end
scenario "territorio vizinho com 3 dados e nao conquistar", js: true do
dado_que_dados_vermelhos_estao_azarentos
#...
end
def dado_que_dados_vermelhos_estao_azarentos
ENV["forcar_vitoria"] = "defesa"
end
def dados_que_dados_vermelhos_estao_sortudos
ENV["forcar_vitoria"] = "ataque"
end
44. Teste do controller
context 'conquistando' do
before :each do
partida.stub!(:executar_ataque) { true }
end
it 'deveria colocar mensagem de sucesso' do
post :create, partida_id: 1, ataque: { pais_atacado: 'argentina' }
flash[:mensagem].should == "Territorio 'argentina' conquistado"
end
end
context 'nao conquistando' do
before :each do
partida.stub!(:executar_ataque) { false }
end
it 'deveria colocar mensagem de insucesso' do
post :create, partida_id: 1, ataque: { pais_atacado: 'argentina' }
flash[:mensagem].should == "Territorio 'argentina' nao foi conquistado"
end
end
45. Controller de ataque
class AtaquesController < ApplicationController
def create
ataque = params[:ataque]
partida = Partida.find params[:partida_id]
pais = ataque[:pais_atacado]
if partida.executar_ataque(ataque)
flash[:mensagem] = "Territorio '#{pais}' conquistado"
else
flash[:mensagem] = "Territorio '#{pais}' nao foi conquistado"
end
redirect_to partida_path(partida)
end
end
46. Metodo burro pro teste passar
class Partida < ActiveRecord::Base
def executar_ataque(attrs)
ENV["forcar_vitoria"] != 'defesa'
end
end
47. O que temos até agora? (3)
Teste View Controller Model
49. Ciclo do outside-in
Crie um teste
end-to-end
Crie um teste
Refatore
Refatore Implemente a solução
50. Conclusão
Sempre guie o desenvolvimento por testes
Tanto por testes de integração como unitários
Combine-os em uma estratégia outside-in
Siga sempre o mantra dos pequenos passos
51. Bibliografia
Test Driven Development: By Example
Kent Beck
Working Effectively with Legacy Code
Michael Feathers
Growing Object-Oriented Software, Guided by Tests
Steve Freeman