Como escrever bons testes! 
por Danilo De Luca e Dherik Barison
“Trying to improve software quality by increasing the 
amount of testing is like trying to lose weight by 
weighing yourse...
Motivação 
• Importância 
• Pouca conversa aprofundada sobre o tema 
• Será que realmente sabemos o fazer um teste 
ser bo...
O que faz um teste ser bom? 
• Performance 
• Manutenabilidade 
• Clareza 
• Resiliência 
• Precisão
Como escrever um teste bom? 
• Testes devem servir como uma documentação do sistema 
• Os nomes dos testes devem dizer o q...
Como conseguir um teste bom? 
Ruim: 
@Test 
public void shouldPerformAddition() { 
Calculator calculator = new Calculator(...
Como conseguir um teste bom? 
Ruim: 
@Test 
public void shouldPerformAddition() { 
Calculator calculator = new Calculator(...
Como conseguir um teste bom? 
Ruim: 
@Test 
public void shouldPerformAddition() { 
Calculator calculator = new Calculator(...
Como conseguir um teste bom? 
Bom: 
@Test 
public void shouldPerformAddition() { 
Calculator calculator = newCalculator();...
Como conseguir um teste bom? 
Bom: 
@Test 
public void shouldPerformAddition() { 
Calculator calculator = newCalculator();...
Como conseguir um teste bom? 
Bom: 
@Test 
public void shouldPerformAddition() { 
Calculator calculator = newCalculator();...
Como conseguir um teste bom? 
Ruim: 
@Test 
public void isUserLockedOut_invalidLogin() { 
authenticator.authenticate(usern...
Como conseguir um teste bom? 
Bom: 
@Test 
public void isUserLockedOut_lockOutUserAfterThreeInvalidLoginAttempts() { 
auth...
Quais as dificuldades mais comuns? 
• Preparação dos cenários 
• Builder e Fluent 
• Load-data 
• Como identificar o que é...
Preparação dos cenários 
• É o local mais comum para ocorrer duplicação 
de código 
• Favorece o reuso desnecessário de ce...
Preparação dos cenários 
Usar patterns como o Builder e o Fluent ajudam, 
pois: 
• Elimina código desnecessário 
• Evita d...
Sem Builder e sem Fluent 
public class CompanyTest { 
@Test 
public void testInsertCompany(){ 
Company company = new Compa...
Builder com Fluent 
public class CompanyTest { 
@Test public void testInsertCompany(){ 
Company company = CompanyBuilder()...
Builder com Fluent 
public class CompanyBuilder { 
private String cnpj = "41.304.875/0001-64"; 
private String name = "Com...
Load-data 
• O que é? 
• Load-data muito grande, com muitos dados, é 
realmente necessário? 
• Usar informações de um load...
Load-data 
• Utilização de FIXTURES para facilitar a 
criação de objetos, evitar consultas ao banco 
e garantir a consistê...
Load-data 
public enum PessoaFixture implements EnumFixture { 
JamesTKirk("JAMES T. KIRK", "JAMES",TipoPessoa.BR,"1986-01-...
Load-data 
@Test 
public void buscarRestricaoPorPessoaQuandoNaoExistirRestricaoParaAPessoa() 
{ 
RestricaoPessoa restricao...
Preparação dos cenários 
• Em testes funcionais ou de integração, é 
comum alguns testes partirem da mesma base 
de inform...
Preparação dos cenários 
• Deixar claro os cenários básicos para reuso! 
Exemplo: 
public class PessoaTeste { 
@Test 
publ...
Preparação dos cenários 
Alguns alertas sobre preparação de cenários: 
Usar com cautela o @BeforeTest e 
@BeforeClass. São...
O que testar? 
• Mesmo com TDD, esta pergunta é válida; 
• Teste comportamentos e não métodos;
Testar comportamento x método 
• Depois de escrever um método, é fácil 
escrever um teste que verifica o que ele faz. 
Mas...
Teste de método 
@Test public void testProcessTransaction() { 
User user = newUserWithBalance(LOW_BALANCE_THRESHOLD.plus(d...
Teste de comportamento 
@Test public void testProcessTransaction_displaysNotification() { 
transactionProcessor.processTra...
Você testa o comportamento de suas exceptions? 
@Test(expectedExceptions = ValidacaoException.class) 
public void validarQ...
Flexibilidade x Simplicidade 
• Evite colocar código de lógica nos seus testes 
• Nos testes, simplicidade é mais importan...
Flexibilidade x Simplicidade 
Ruim: 
@Test 
public void shouldNavigateToPhotosPage() { 
String baseUrl = "http://plus.goog...
Flexibilidade x Simplicidade 
Bom: 
@Test 
public void shouldNavigateToPhotosPage() { 
Navigator nav = new Navigator(“http...
“... if it seems like a simple change to code 
causes excessively long changes to tests, that's 
a sign that there's a pro...
Cobertura de Testes 
• Porcentagem de cobertura é importante? 
• A cobertura tem seus méritos!
Cobertura de Testes 
• Encontrar código não testado 
• Ter um senso de qual a situação real do 
projeto
E o TDD?! 
• O que é? 
• No que facilita? 
• Qual a dificuldade de usar? 
• Polêmicas!
TDD 
“If you don’t drive development with tests, what do 
you drive it with? Speculation? Specifications (ever 
notice tha...
…e os nossos clientes?? 
Como nossos clientes veem a questão dos 
testes? 
Eles acreditam neles tanto quanto nós 
acredita...
Conclusão 
• Simplicidade é muito importante 
• Se algum teste está difícil de manter, ele não 
está bom 
• Trate com cari...
O que ficou faltando falar... 
• Muita coisa! 
• Quando usar frameworks de mock (Mockito, EasyMock, 
etc) 
• Testes de tel...
Referências 
• Google Testing Blog 
• Livro Code Complete 
• Métodos ágeis: o que é flolclore e o que é real 
• Is TDD dea...
Próximos SlideShares
Carregando em…5
×

Como escrever bons testes! - Dex transforming days

497 visualizações

Publicada em

Um workshop que eu e Dherik Barison fizemos para levantar alguns pontos sobre como escrever bons testes, também levantamos alguns argumentos sobre o que existe no mundo de sotfware quando o tema é Testes, um workshop para trazer discussões das pessoas que o assistiram, para que conseguíssemos articular os assuntos de uma forma aberta, trazendo muitas vezes mais questionamentos do que respostas para os participantes
=D

Publicada em: Software
0 comentários
3 gostaram
Estatísticas
Notas
  • Seja o primeiro a comentar

Sem downloads
Visualizações
Visualizações totais
497
No SlideShare
0
A partir de incorporações
0
Número de incorporações
11
Ações
Compartilhamentos
0
Downloads
2
Comentários
0
Gostaram
3
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide
  • Mostrar que não deve existir diferença entre qualidade de código de um teste para um código de “business”.
    http://googletesting.blogspot.com.br/2014/05/testing-on-toilet-effective-testing.html
    a resilient test doesn't have to change unless the purpose or behavior of the class being tested changes. Adding new behavior should only require adding new tests, not changing old ones. The original test above isn't resilient since you'll have to update it (and probably dozens of other tests!) whenever you add a new irrelevant constructor parameter. Moving these details into the helper method solved this problem.
  • * casos raros, como garantir que o sistema está lendo do cache e não do banco de dados
  • Detalhes desnecessários foram escondidos. O método makeAdditionComputation deixa mais claro.
  • O método newCalculator() abstrai toda a logica de como instanciar uma “calculadora” deixando o teste simples e claro para quem precisar le-lo
  • O método newCalculator() abstrai toda a logica de como instanciar uma “calculadora” deixando o teste simples e claro para quem precisar le-lo
  • Esperar para ver se o pessoal consegue identificar o que este teste faz! http://googletesting.blogspot.com.br/2014/10/testing-on-toilet-writing-descriptive.html
  • https://github.com/dextra/kroton_empregabilidade/blob/master/implementation/web/src/test/java/br/com/kroton/domain/builder/CompanyBuilder.java
  • traduzir!
  • traduzir!
  • http://googletesting.blogspot.com.br/2014/07/testing-on-toilet-dont-put-logic-in.html
  • a resilient test doesn't have to change unless the purpose or behavior of the class being tested changes. Adding new behavior should only require adding new tests, not changing old ones. The original test above isn't resilient since you'll have to update it (and probably dozens of other tests!) whenever you add a new irrelevant constructor parameter. Moving these details into the helper method solved this problem.
  • Argumentar sobre essa frase, que é uma das justificativas para se usar TDD

    Falar sobre o “is tdd dead”
  • Argumentar sobre essa frase, que é uma das justificativas para se usar TDD

    Falar sobre o “is tdd dead”
  • Como escrever bons testes! - Dex transforming days

    1. 1. Como escrever bons testes! por Danilo De Luca e Dherik Barison
    2. 2. “Trying to improve software quality by increasing the amount of testing is like trying to lose weight by weighing yourself more often” Steve McConnell - Code Complete
    3. 3. Motivação • Importância • Pouca conversa aprofundada sobre o tema • Será que realmente sabemos o fazer um teste ser bom... • Falta de visibilidade de que o código de um teste deve ser tão bom quanto um código de “business”
    4. 4. O que faz um teste ser bom? • Performance • Manutenabilidade • Clareza • Resiliência • Precisão
    5. 5. Como escrever um teste bom? • Testes devem servir como uma documentação do sistema • Os nomes dos testes devem dizer o que eles fazem • Não deve conhecer detalhes de implementação* • Não deve exibir informações que distraiam do objetivo do teste • Um novo comportamento na aplicação não deve alterar os testes antigos, apenas adicionar novos testes
    6. 6. Como conseguir um teste bom? Ruim: @Test public void shouldPerformAddition() { Calculator calculator = new Calculator(new RoundingStrategy(), "unused", ENABLE_COSIN_FEATURE, 0.01, calculusEngine, false); int result = calculator.doComputation(makeTestComputation()); assertEquals(5, result); // De onde este número veio? }
    7. 7. Como conseguir um teste bom? Ruim: @Test public void shouldPerformAddition() { Calculator calculator = new Calculator(new RoundingStrategy(), "unused", ENABLE_COSIN_FEATURE, 0.01, calculusEngine, false); int result = calculator.doComputation(makeTestComputation()); assertEquals(5, result); // De onde este número veio? }
    8. 8. Como conseguir um teste bom? Ruim: @Test public void shouldPerformAddition() { Calculator calculator = new Calculator(new RoundingStrategy(), "unused", ENABLE_COSIN_FEATURE, 0.01, calculusEngine, false); int result = calculator.doComputation(makeTestComputation()); assertEquals(5, result); // De onde este número veio? }
    9. 9. Como conseguir um teste bom? Bom: @Test public void shouldPerformAddition() { Calculator calculator = newCalculator(); int result = calculator.doComputation(makeAdditionComputation(2,3)); assertEquals(5, result); }
    10. 10. Como conseguir um teste bom? Bom: @Test public void shouldPerformAddition() { Calculator calculator = newCalculator(); int result = calculator.doComputation(makeAdditionComputation(2, 3)); assertEquals(5, result); }
    11. 11. Como conseguir um teste bom? Bom: @Test public void shouldPerformAddition() { Calculator calculator = newCalculator(); int result = calculator.doComputation(makeAdditionComputation(2, 3)); assertEquals(5, result); }
    12. 12. Como conseguir um teste bom? Ruim: @Test public void isUserLockedOut_invalidLogin() { authenticator.authenticate(username, invalidPassword); assertFalse(authenticator.isUserLockedOut(username)); authenticator.authenticate(username, invalidPassword); assertFalse(authenticator.isUserLockedOut(username)); authenticator.authenticate(username, invalidPassword); assertTrue(authenticator.isUserLockedOut(username)); }
    13. 13. Como conseguir um teste bom? Bom: @Test public void isUserLockedOut_lockOutUserAfterThreeInvalidLoginAttempts() { authenticator.authenticate(username, invalidPassword); assertFalse(authenticator.isUserLockedOut(username)); authenticator.authenticate(username, invalidPassword); assertFalse(authenticator.isUserLockedOut(username)); authenticator.authenticate(username, invalidPassword); assertTrue(authenticator.isUserLockedOut(username)); }
    14. 14. Quais as dificuldades mais comuns? • Preparação dos cenários • Builder e Fluent • Load-data • Como identificar o que é bom ser testado (tudo?). Como criar estes testes?
    15. 15. Preparação dos cenários • É o local mais comum para ocorrer duplicação de código • Favorece o reuso desnecessário de cenários • O que podemos fazer?
    16. 16. Preparação dos cenários Usar patterns como o Builder e o Fluent ajudam, pois: • Elimina código desnecessário • Evita duplicação • Incentiva o reuso dos mesmos valores de variáveis entre os testes
    17. 17. Sem Builder e sem Fluent public class CompanyTest { @Test public void testInsertCompany(){ Company company = new Company("Mc Donalds", "41.304.875/0001-64"); //teste de inserir } @Test public void testUpdateCompany(){ Company company = new Company("Company 1", "58.455.457/0001-70"); //teste de atualização } @Test public void testRemoveCompany(){ Company company = new Company("ACME", "68.132.955/0001-01"); //teste de remoção } }
    18. 18. Builder com Fluent public class CompanyTest { @Test public void testInsertCompany(){ Company company = CompanyBuilder().build(); //teste de inserir } @Test public void testUpdateCompany(){ Company company = CompanyBuilder().build(); //teste de atualização } @Test public void testRemoveCompany(){ Company company = CompanyBuilder().withName(“Dextra”).build(); //teste de remoção } }
    19. 19. Builder com Fluent public class CompanyBuilder { private String cnpj = "41.304.875/0001-64"; private String name = "Company Name"; public Company build() { return new Company().withCnpj(cnpj).withName(name); } public CompanyBuilder withCnpj(String cnpj) { this.cnpj = cnpj; return this; } public CompanyBuilder withName(String name) { this.name = name; return this; } }
    20. 20. Load-data • O que é? • Load-data muito grande, com muitos dados, é realmente necessário? • Usar informações de um load-data para um teste é bom? • Se for utilizar um load-data, como fazer isso de uma maneira fácil para entendimento e construção de um teste?
    21. 21. Load-data • Utilização de FIXTURES para facilitar a criação de objetos, evitar consultas ao banco e garantir a consistência dos dados na utilização em testes.
    22. 22. Load-data public enum PessoaFixture implements EnumFixture { JamesTKirk("JAMES T. KIRK", "JAMES",TipoPessoa.BR,"1986-01-01"); public String toInsertString() ; public Long getId(); public Pessoa toEntidade() { return EnumFixtureUtil.preencherAtributos(new Pessoa(), this); } }
    23. 23. Load-data @Test public void buscarRestricaoPorPessoaQuandoNaoExistirRestricaoParaAPessoa() { RestricaoPessoa restricao = repository.buscarPorPessoa(PessoaFixture.JamesTKirk.toEntidade()); Assert.assertNull(restricao); }
    24. 24. Preparação dos cenários • Em testes funcionais ou de integração, é comum alguns testes partirem da mesma base de informações. Exemplo de uma aplicação bancária: exige uma Pessoa, Pessoa Física, Usuário, Conta Corrente… Como lidar?
    25. 25. Preparação dos cenários • Deixar claro os cenários básicos para reuso! Exemplo: public class PessoaTeste { @Test public void testeMovimentacaoContaCorrente { criarCenarioBasicoPessoaComContaCorrente(); //codigo do teste } public void criarCenarioBasicoPessoaComContaCorrente() { // codigo do cenario } }
    26. 26. Preparação dos cenários Alguns alertas sobre preparação de cenários: Usar com cautela o @BeforeTest e @BeforeClass. São usados apenas quando você tem absoluta certeza que TODOS os testes precisam do código que pretende colocar lá sob estas anotações.
    27. 27. O que testar? • Mesmo com TDD, esta pergunta é válida; • Teste comportamentos e não métodos;
    28. 28. Testar comportamento x método • Depois de escrever um método, é fácil escrever um teste que verifica o que ele faz. Mas complica se achar que os testes e métodos público devem ter relação 1:1 • Nós queremos realmente é testar o seu comportamento!
    29. 29. Teste de método @Test public void testProcessTransaction() { User user = newUserWithBalance(LOW_BALANCE_THRESHOLD.plus(dollars(2)); transactionProcessor.processTransaction(user, new Transaction("Pile of Beanie Babies", dollars(3))); assertContains("You bought a Pile of Beanie Babies", ui.getText()); assertEquals(1, user.getEmails().size()); assertEquals("balance is low”, user.getEmails().get(0).getSubject()); }
    30. 30. Teste de comportamento @Test public void testProcessTransaction_displaysNotification() { transactionProcessor.processTransaction( new User(), new Transaction("Pile of Beanie Babies")); assertContains("You bought a Pile of Beanie Babies", ui.getText()); } @Test public void testProcessTransaction_sendsEmailWhenBalanceIsLow() { User user = newUserWithBalance(LOW_BALANCE_THRESHOLD.plus(dollars(2)); transactionProcessor.processTransaction( user, new Transaction(dollars(3))); assertEquals(1, user.getEmails().size()); assertEquals("Your balance is low", user.getEmails().get(0).getSubject()); }
    31. 31. Você testa o comportamento de suas exceptions? @Test(expectedExceptions = ValidacaoException.class) public void validarQuandoNaoTemDataBloqueio() throws ValidacaoException { Date dataBloqueio = null; RestricaoPessoa restricao = new RestricaoPessoa(new Brasileiro(), dataBloqueio, OrigemRestricaoPessoa.SOCC, new TipoRestricaoPessoa()); restricao.validar(); } @Test(expectedExceptions = PagamentoMaiorQueTotalAPagarException.class) public void addPagamentoEspecieTendoValorMaiorQueDaProposta() throws PagamentoMaiorQueTotalAPagarException,PagamentoExcedenteException { proposta.valor(BigDecima.valueOf(50l); PagamentoEspecie pagamento = new PagamentoEspecie(MoedaFixture.USD, BigDecimal.valueOf(100l), proposta, new Venda()); proposta.addPagamento(pagamento); }
    32. 32. Flexibilidade x Simplicidade • Evite colocar código de lógica nos seus testes • Nos testes, simplicidade é mais importante que flexibilidade!
    33. 33. Flexibilidade x Simplicidade Ruim: @Test public void shouldNavigateToPhotosPage() { String baseUrl = "http://plus.google.com/"; Navigator nav = new Navigator(baseUrl); nav.goToPhotosPage(); assertEquals(baseUrl + "/u/0/photos", nav.getCurrentUrl()); }
    34. 34. Flexibilidade x Simplicidade Bom: @Test public void shouldNavigateToPhotosPage() { Navigator nav = new Navigator(“http://plus.google.com/"); nav.goToPhotosPage(); assertEquals("http://plus.google.com//u/0/photos", nav.getCurrentUrl()); // Oops! }
    35. 35. “... if it seems like a simple change to code causes excessively long changes to tests, that's a sign that there's a problem with the tests. This may not be so much that you are testing too many things, but that you have duplication in your tests….” - Kent Beck Resiliência
    36. 36. Cobertura de Testes • Porcentagem de cobertura é importante? • A cobertura tem seus méritos!
    37. 37. Cobertura de Testes • Encontrar código não testado • Ter um senso de qual a situação real do projeto
    38. 38. E o TDD?! • O que é? • No que facilita? • Qual a dificuldade de usar? • Polêmicas!
    39. 39. TDD “If you don’t drive development with tests, what do you drive it with? Speculation? Specifications (ever notice that those two words come from the same root?)” Kent Beck - TDD by Example
    40. 40. …e os nossos clientes?? Como nossos clientes veem a questão dos testes? Eles acreditam neles tanto quanto nós acreditamos? Por que quando temos que priorizar algumas coisas, acabamos por deixar os testes de lado em algumas situações?
    41. 41. Conclusão • Simplicidade é muito importante • Se algum teste está difícil de manter, ele não está bom • Trate com carinho o código do teste • Escreva seu código de teste pensando como um código de business
    42. 42. O que ficou faltando falar... • Muita coisa! • Quando usar frameworks de mock (Mockito, EasyMock, etc) • Testes de tela: PhantonJS, Selenium, etc • Minimização de bugs não-reproduzíveis • Diferença entre fake, mock, stub e dummy • Testes de carga
    43. 43. Referências • Google Testing Blog • Livro Code Complete • Métodos ágeis: o que é flolclore e o que é real • Is TDD dead?

    ×