Download - Palestra Mocks - AgileBrazil 2010
Já que vai fingir, finja direitoTestando com Mocks
Lucas Cavalcanti @lucascsRafael Ferreira @rafaeldff
Testes unitários
Seu código é testável?
public class Programa { public static void executa() { BancoDeDados bd = new BancoDeDados(); EnviadorDeEmail enviador =
new EnviadorDeEmail(); Usuario usuario = Usuario.getAtual(); if (bd.autentica(usuario)) { enviador.enviaEmail(usuario); } }}
Injeção de dependências
Código mais testável
public class Mecanico {
public Mecanico(ChaveDeFenda fenda, ChaveInglesa inglesa,CalendarioPorno calendario) {
... }
public void trocaMotor(Carro carro) { try { Motor motor = carro.getMotor(); fenda.remove(motor); } catch (TopadaNoDedaoException e) { xingaMuitoNoTwitter(e); } }
}
Teste #comofas
Como criar um Mecanico?
ChaveDeFenda fenda = vaiNaCaixaEBuscaAChaveDeFenda();
ChaveInglesa inglesa = vaiNaCaixaEBuscaAChaveInglesa();
CalendarioPorno calendario = vaiNaLojaECompraOCalendario();
Mecanico mecanico = new Mecanico(fenda, inglesa, calendario);
//resto do teste...
Mocks
Como criar um Mecanico?
ChaveDeFenda fenda = mock(ChaveDeFenda.class);
ChaveInglesa inglesa = mock(ChaveInglesa.class);
CalendarioPorno calendario = mock(CalendarioPorno.class);
Mecanico mecanico = new Mecanico(fenda, inglesa, calendario);
//resto do teste...
Test Smells
Quando seu teste diz que há algo de errado
Nomes ruins
public class LancadorTest {
public void testDispara() { // coisas complicadas }
}
Nomes ruins
public class LancadorTest {
public void disparaMisseisQuandoUmAtaqueNuclearForDetectado(){ // coisas escabrosas }
}
X
Testes muito grandes
public void testEnaganaOCliente() { Parafuseta parafuseta = new Parafuseta(new Estepe()); List<Cavalo> cavalos = Collections.nCopies(123, new Cavalo(Cor.BRANCO)); parafuseta.setMotor(new Motor(cavalos)); Peca rebimbocaDaParafuseta = new Rebimboca(parafuseta); Cliente cliente = mockery.mock(Cliente.class); mockery.checking(new Expectations() {{ one(cliente).recebePreco(Double.MAX_VALUE); one(cliente).recebePreco(Double.MAX_VALUE / 2); one(cliente).naoTemUmJeitinho(true); one(cliente).recebePreco(1.99d); }}); Mecanico mecanico = new Mecanico(); mecanico.forneceOrcamento(rebimbocaDaParafuseta, cliente);}
Testes muito grandes
public void testEnganaOCliente() { Cliente cliente = mockery.mock(Cliente.class); esperandoNegociacao(cliente);
mecanico.forneceOrcamento(umaPecaInventada(), cliente);}
public void esperandoNegociacao(Cliente cliente) { mockery.checking(new Expectations() {{ one(cliente).recebePreco(Double.MAX_VALUE); one(cliente).recebePreco(Double.MAX_VALUE / 2); one(cliente).naoTemUmJeitinho(true); one(cliente).recebePreco(1.99d); }});
}
public Peca umaPecaInventada() { Parafuseta parafuseta = new Parafuseta(new Estepe()); List<Cavalo> cavalos = Collections.nCopies(123, new Cavalo(Color.BRANCO)); parafuseta.setMotor(new Motor(cavalos)); return new Rebimboca(parafuseta);
}X
Muitas dependências
public void disparaMisseisNuclearesQuandoAtaqueForDetectado() { DetectorAereo detectorAereo = mock(DetectorAereo.class); DetectorDeSubmarino detectorDeSubmarino = mock(DetectorDeS... ComandoRemoto comandoRemoto = mock(ComandoRemoto.class); SensorDePopularidade sensorDePopularidade = mock(SensorDePo... Gps gpsDoBinLaden = mock(Gps.class); Rebimboca rebimbocaDaParafuseta = mock(Rebimboca.class);
Lancador lancador = new Lancador(detectorAereo, detectorDeSubmarino, comandoRemoto, sensorDePopularidade, gpsDoBinLaden, rebimbocaDaParafuseta);
// coisas cabeludas...}
Muitas dependências
public void disparaMisseisNuclearesQuandoAtaqueForDetectado() { DetectorDeAtaque detectorDeAtaque = mock(DetectorDeAtaque... Rebimboca rebimbocaDaParafuseta = mock(Rebimboca.class);
Lancador lancador = new Lancador(detectorDeAtaque, rebimbocaDaParafuseta);
// coisas do arco da velha...}
X
Dependências escondidaspublic class Pedido { private static final int TRES_HORAS = 3*60*60*1000; private Date dataDeCriacao = new Date();
public boolean isExpirado() { long agora = System.currentTimeMillis(); return agora - dataDeCriacao.getTime() < TRES_HORAS; }}
public void testaSeEstaExpiradoDepoisDeTresHoras() throws Exception {
Pedido pedido = new Pedido();
Thread.sleep(TRES_HORAS); //?!
assertTrue(pedido.isExpirado());}
Dependências escondidaspublic class Pedido { private final Date dataDeCriacao; public Pedido(Relogio relogio) { this.dataDeCriacao = relogio.agora(); }
public boolean isExpirado(Date data) { return data.getTime() - dataDeCriacao.getTime() < TRES_HORAS; }}
public void testaSeEstaExpiradoDepoisDeTresHoras() { final Relogio relogio = mockery.mock(Relogio.class);
mockery.checking(new Expectations() {{ one(relogio).agora(); will(returnValue(tresHorasAtras()); }});
Pedido pedido = new Pedido(relogio); assertTrue(pedido.isExpirado(agora()));}X
Testes frágeis
@Testpublic void enviaEmailDeSenha() { Usuario usuario = new Usuario(); mockery.checking(new Expectations() {{ one(dao).buscaUsuario(1); one(template).compoeEmailDeSenha(usuario); one(configuracao).servidorSmtp(); one(enviadorDeEmail).envia(servidor, email, usuario); }}); controller.enviaEmailDeSenha(1);}
Testes frágeis
@Testpublic void enviaEmailDeSenha() { Usuario usuario = new Usuario(); mockery.checking(new Expectations() {{ one(enviadorDeEmail).envia(servidor, email, usuario); }}); controller.enviaEmailDeSenha(1);}
@Beforepublic void setUp() { mockery.checking(new Expectations() {{ allowing(dao).buscaUsuario(1); allowing(template).compoeEmailDeSenha(usuario); allowing(configuracao).servidorSmtp(); }});}
X
Stubs
Estilos de teste com Mocks
Behavior Driven Development
desenvolvendo orientado ao comportamento
Given
@Testpublic void shouldRollbackActiveTransactions(){ given(transaction.isActive()).willReturn(true);
manager.persist(ANY_ENTITY);
verify(transaction).rollback();}
When
Then
Obrigado!
Lucas Cavalcanti @lucascsRafael Ferreira @rafaeldff
http://www.caelum.com.br