sobre o código e design • Refatoração como parte do ciclo • Geração de modelos pra suportar o teste • Análise do domínio • Análise do limite (boundary) • Teste estrutural como complemento • Teste inteligente (AI, análise estática, mutante)
sobre o código e design • Refatoração como parte do ciclo • Geração de modelos pra suportar o teste • Análise do domínio • Análise do limite (boundary) • Teste estrutural como complemento • Teste inteligente (AI, análise estática, mutante, fuzzing) Todo mundo fala sobre na comunidade! Poucos falam sobre na comunidade!
te ajudarão no teste. • Modelos abstraem a complexidade do sistema, e te ajudam a ver o que importa. • (“Ah, mas agile fala pra não fazer documentação… Meus amigos vão me chamar de Waterfallzeiro se eu committar algo que não é código!) • Claro que só onde faz sentido!
um grande número de bugs acontecem nas “fronteiras” do domínio. • Teste os limites! • Tem um if(x > 10), testa o que acontece entre 9, 10, 11. • Tem um for? Testa 0, 1, N. • Analisar variável por variável por ajudar. • Você pensa em todas fronteiras quando faz TDD? • Eu não! L https://sttp.site/chapters/testing-techniques/domain-testing.html
é fácil de enganar, patati patató”) • Code coverage não é sobre um número. • É sobre aumentar a sua suíte de testes (que você criou baseado no que sabe do requisito) baseado no que você vê no código. • A ferramenta de cobertura pode te ajudar a encontrar códigos que não são explícitos no requisito. https://sttp.site/chapters/testing-techniques/structural-testing.html
especial quando dependências são difíceis ou caras de serem simuladas. • Escolha o tipo de dublê certo: • Fake: uma implementação simplística • Stubs: retorna valores hard-coded • Mocks: grava as interações com o objeto, pq depois você vai perguntar se elas aconteceram! • Decidir ou não se mockar é uma boa ideia! https://sttp.site/chapters/pragmatic-testing/test-doubles.html
TDD. • Classical vs London • State vs Interaction testing • State testing: eu testo a “mudança de estado” do meu objeto. • Invoco método A.a(), não ligo pra como A.a() faz o trabalho, ou de quem ele depende, faço a asserção para garantir que A tem um novo estado. • Interaction testing: eu testo “interação” entre dependências • Invoco método A.a(). A depende de uma abstração B. Eu garanto que A invoca B da maneira correta. • Pensa nos seus Mockito.verify(). Você está garantindo a integração.
você tá testando o mock!” • Problema: • SuperServico depende de RepositorioDeNotasFiscais#pegaTodasDoMes(). • pegaTodasDoMes() nunca retorna nulo. • Quando testa SuperServico, desenvolvedor mocka pegaTodasDoMes() • Testes passam • No futuro, desenvolvedor 2 faz pegaTodasDoMes() retornar nulo. • Testes passam, mas em produção quebra. • O problema é manter o mock com o mesmo contrato da classe original. • Difícil mockar se design é inconstante. Requer times com bons designers!
seus métodos e serviços oferecem pro mundo de fora. • Pré-condições, pós-condições, invariantes. • Deixe-as explícitas no código! • Mudou alguma delas? Análise em cascata! • Hyrum’s law: “Dado um número suficiente de clientes, não importa o que você promete no contrato. Todo e qualquer comportamento observado no sistema será dependido por alguém”. https://www.hyrumslaw.com https://sttp.site/chapters/testing-techniques/design-by-contracts.html
máquina só executou! • Pq não deixar a máquina criar uns testes pra você também? • Não, não é ficção! • Não, AI não vai dominar o mundo! • Pense em: • Análise estática • Testes de mutantes • Fuzz testing • Geração automática de casos de testes https://sttp.site/chapters/intelligent-testing/
aquela que te revela bugs, quando bugs acontecem. • Mutante: uma pequena variação (bugada) do seu código. Seu teste falha? • Imagina um “if(x>10)”. Se eu trocasse por “if(x>=10)”, algum teste falharia? • Sim? Ótimo. Não? Talvez valha a pena reforçar a sua suíte de teste. • Hipótese: • código é escrito por um programador “competente” e erros acontecem por combinações de erros simples. • “Falhas complicadas” acontecem por causa de “falhas simples”. https://sttp.site/chapters/intelligent-testing/mutation-testing.html
um caso de teste é: • Cria a classe, pelo construtor. • Invoca um método. • Passa valores pra esse método • Faz a asserção no final. • A máquina pode criar código como esse! https://sttp.site/chapters/intelligent-testing/sbst.html
tem que produzir? • Difícil… • Mas sabemos de muitas coisas, independente do programa. • Ele não pode soltar exceção. • A app mobile não pode congelar. • Ele não pode demorar mais que X segundos. • Até contextual: Ele não pode retornar nota fiscal com valor negativa… • Esse tipo de bug, a máquina pode facilmente procurar pra você!
Invista em infraestrutura de testes • Leia sobre design patterns e code smells em códigos de teste • Property-based testing • Boa ideia, em especial quando combinado com DbC • (Eu tenho pouca experiência prática…) • Design for testability • Boas práticas de arquitetura para facilitar testes. • Fundamental!! • Decidir nível do teste • Sinto que todo mundo já tem um bom feeling de quando usar teste de unidade ou teste de sistema