Tag Archives: tdd

Primeiros Passos com Testes de Unidade – Parte 2

No post passado, escrevemos testes para o FizzBuzz. E foi fácil: criamos a classe FizzBuzzTest e escrevemos lá métodos, com diferentes entradas e saídas. A grande charada é que, como conhecemos a entrada, também sabemos a saída. Mas será que os testes que temos são suficientes? Vamos agora discutir um pouco sobre o que testar.

Veja novamente nosso código de produção:

O grande truque é olhar para o código e pensar nos vários caminhos diferentes que ele pode ter. Para isso, analisarmos cada instrução que faz o fluxo do código mudar. Por exemplo, ifs, fors, whiles, etc. Em nosso FizzBuzz, temos:

  • Cenário 1: O primeiro if tem duas condições: número é múltiplo de 5 E múltiplo de 7. Se esse if der verdadeiro, o programa retorna “fizzbuzz” e acaba.
  • Cenário 2: O segundo if valida se o número é múltiplo de 5. Se der verdadeiro, o programa retorna “fizz” e acaba.
  • Cenário 3: O terceiro if valida se o número é múltiplo de 7. Se der verdadeiro, o programa retorna “buzz” e acaba.
  • Cenário 4: O quarto caminho acontece quando nenhum dos anteriores acontece. O programa retorna o número e acaba.

O próximo passo é: qual a entrada que precisamos passar para que o cenário 1 aconteça? E depois, para o cenário 2? E assim por diante. Foi o que fizemos: passamos 35 para o cenário 1 acontecer, ou 13 para o cenário 4 acontecer.

E perceba também que temos um teste para cada cenário. Preciso de mais? Geralmente não. Se você pensar bem nos cenários, verá que não precisará exercitar mais de uma entrada em cada. Chamamos essas entradas diferentes, mas que exercitam o código da mesma maneira, de classes de equivalência. Um teste por classe de equivalência é uma boa regra a seguir.

No entanto, nada te impede de ter um pouco mais de segurança. Quando lidamos com algoritmos que envolvem números, como é o caso desse, podemos exercitar cenários similares. Não só para garantir que funciona, mas também para facilitar o entendimento quando outro desenvolvedor ler o teste.

E casos excepcionais? Se você achar que seu programa precisa tratar casos excepcionais, como zero, nulo ou exceção, você então deve escrever o teste para garantir que seu programa se comportará corretamente.

Neste post, discuti sobre como deve ser seu pensamento na hora de escrever um teste. Mas ainda temos 3 posts. Estamos chegando lá! :)

Classes testáveis não “buscam”, mas sim “recebem”

TL;DR: Se você quer testar sua classe por meio de teste de unidade, essa classe não deve conter código de infra estrutura (como acesso a banco de dados, e etc), e ela também deve receber toda outra informação ou dependência necessária por meio de construtores ou parâmetros de métodos — a classe nunca deve buscar a informação diretamente ou instanciar uma dependência. Dessa forma, ela será facilmente testável por meio de testes de unidade.

Regras de negócio, no fim, nada mais são do que um monte de ifs e fors juntos. Portanto, deveríamos ser capazes de sempre testá-las por meio de simples testes de unidade. No entanto, às vezes nós dificultamos isso.

Veja o código abaixo. Nele, o FiltroDeFatura pega a lista de todas as faturas que está no banco de dados, e passeia por elas, guardando as que tem valores menor que 2000.

O código é simples, mas pense que ele poderia ser um pouco mais complicado. Portanto, precisamos testá-lo. A questão é: como? Afinal, não conseguimos escrever um teste de unidade pra ela; afinal é impossível executar o método filtra() sem passar por um banco de dados.

Essa é um tipo de código bastante comum nas aplicações por aí. O desenvolvedor sabe que não pode sair misturando SQL no meio de regra de negócio, e corretamente, coloca isso dentro do DAO. No entanto, ele instancia o DAO diretamente na classe, fazendo com que não seja possível executar a regra de negócio, sem passar pelo banco de dados.

E passar pelo banco de dados, na maioria das vezes, não é boa ideia. Escrever o teste é mais difícil (afinal, precisamos fazer INSERT dos dados no começo, DELETE depois, garantir o schema do banco, e etc), e mais demorado. E, aliás, não deveríamos precisar do banco de dados, para testar a simples regra de filtro de fatura.

A solução pra isso é mais fácil do que parece. Se você tem uma classe que contém regras de negócio, essa classe deve apenas conter regras de negócio. Ou seja, ifs e fors. Se sua regra de negócio precisar de alguma informação que venha de uma outra classe qualquer (seja um DAO, seja outra coisa), ela nunca deve “buscar” essa informação, mas sim “recebê-la”.

Ou seja, nesse código em particular, ou passamos a List<Fatura> para o método filtra(), ou passamos o FaturaDao pelo construtor. Veja:

Dessa forma, com o DAO sendo recebido pelo construtor da classe, conseguimos simular seu comportamento durante o teste, por meio de mock objects. E agora sim, nada de depender do banco para fazer um teste tão simples.

Portanto, guarde essa regra: se sua classe é uma classe que contém regras de negócio, ela nunca pode buscar as informações ou dependências que precisa por conta própria; ela deve sempre recebê-las.

Ah, essa ideia tem um nome bonito, inclusive: chama-se inversão de controle. Ou seja, “invertemos” a maneira tradicional de programar, que é sempre buscar pela dependência. Agora, alguém nos dá a dependência. Você pode ler mais sobre isso no meu livro de orientação a objetos e SOLID.

Montando cenários em testes de sistema

TL;DR: Crie serviços web para montar os cenários que você precisa em seus testes de aceitação.

Montar cenários é sempre a parte mais chata em qualquer teste, seja ele manual, de unidade, de integração ou de sistema. O pior é que, quanto mais o teste é parecido com o do mundo real, mais difícil é. No teste de unidade, você tem uma classe, geralmente pequena, e que lida com poucas outras classes. Montar o cenário é razoavelmente fácil; você instancia duas ou três classes e pronto. Mas e quando o teste é de sistema? Como fazer para testar a tela de Nota Fiscal, se para isso, precisamos de um Cliente, Produto e Orçamento cadastrados?

A primeira opção é fazer com que o próprio teste seja responsável por navegar pelas telas anteriores e montar os cenários. Ou seja, antes de ir pra tela de nota fiscal, o teste passeia pela teste de clientes, e cadastra um por lá. Depois vai pra tela de Produto e cadastra um por lá. A mesma coisa com um Orçamento, para aí sim conseguir testar a Nota Fiscal. O problema disso é que absurdamente demorado passear por todas essas telas. E a chance de você repetir código entre as várias baterias de teste é ainda maior.

Nesse momento, meu coração diz que a melhor solução é fazer com que a aplicação Web contenha um conjunto de serviços web, prontos para montar o cenário que a aplicação quiser.

  • Precisa de um Cliente? O teste faz um post para /ws-testes/clientes, com o Cliente desejado.
  • Precisa de um Orçamento? O teste faz um post para /ws-testes/orcamentos.
  • Precisa de um Estado (e não existe tela de estado?) Não tem problema, tem o /ws-testes/estados.

Dessa forma, fica fácil. No código do teste, você faz uso de Test Data Builders para rapidamente montar esses objetos, e simplesmente os envia para a aplicação. Fazer serviços web simples, que recebem basicamente entidades, também é fácil, e seu framework MVC deve fazer isso quase que automático.

Além disso, porquê não ter também o /ws-testes/limpa ? Precisamos limpar nosso banco de dados antes de cada teste, para que um teste não influencie o outro.

Apesar de parecer pouco produtivo, na prática, os Test Data Builders usados são os mesmos que você já usa nos seus testes de unidade. E se você tem uma infraestrutura razoável, fazer serviços web é geralmente mais simples do que escrever a navegação nas funcionalidades ao redor, que você precisaria escrever de qualquer jeito.

Fuja dos XMLs malucos do DBUnit ou mesmo de testes de sistema que precisam navegar por 30 telas antes da sua. Dá trabalho? Claro que dá… Mas testar software é trabalho, não é?!

Para melhorar meu design, preciso mesmo fazer o teste antes?

Já comentei bastante sobre os efeitos que a prática de TDD tem no projeto de classes. Meu livro, aliás, é totalmente focado nisso. No entanto, algo que não comentei nele, porquê provavelmente só soube transformar isso em palavras há pouco tempo, é que não me importo muito se o teste é escrito antes ou depois. Ué, como assim!?

Deixe-me explicar. Não é a “prática de TDD” que faz seu projeto de classes melhorar, mas sim, o que você consegue ver de problemas ao olhar para o código do seu teste. Ou seja, é o teste que te mostra o problema. Se ele é escrito antes ou depois, tanto faz.

Obviamente, se você deixar para escrever o teste 1 semana depois de escrever a classe, na hora em que ler o feedback do teste, talvez seja tarde demais pra refatorar — tarde, no sentido, de caro, afinal você provavelmente precisará mudar em muitos pontos diferentes.

No entanto, se você gastar 15 minutos no código, 15 minutos no teste, 15 minutos no código, 15 minutos no teste…. o feedback será o mesmo! Afinal, para pensar no design, a implementação depende do teste e o teste depende da implementação; a ordem em que você faz não ter dará mais ou menos retorno.

O teste é só um rascunho do seu projeto de classes. E o importante não é ter um rascunho, ou mesmo começar por ele, mas sim rascunhar com frequência.

 

Mockar ou não mockar?

Com toda essa discussão do “Is TDD Dead?”, criada pelo DHH, as pessoas começaram a se perguntar se devem ou não fazer uso de mocks. Apesar do DHH ter sido infeliz no post polêmico, a discussão sobre mocks que ele levantou é bem importante. Tanto é que eu e o Hugo Corbucci fizemos uma palestra dedicada ao uso de mock objects, ano passado, na Agile Brazil.

Nela, discutimos as vantagens e desvantagens de usar ou não usar mocks, quando usar, quando não usar, e etc. Apesar da gravação não ter sido das melhores, é possível aprender bastante com ela.

Espero que gostem!

Mockar ou não mockar? – Hugo Corbucci e Mauricio Aniche from Agile Brazil on Vimeo.

Bate papo sobre TDD na Caelum

Há alguns meses atrás, organizei na Caelum um encontro sobre TDD. Nela tivemos a participação do Anderson Parra, falando sobre Fixture Factory (que divulgarei em breve também!), e depois um bate papo sobre pontos gerais de testes e TDD.

Abaixo segue o vídeo com a discussão na íntegra:

Se você gostou da discussão, não deixe de ver meu livro ou os cursos online do Alura sobre o assunto.

A quantidade de asserts em um teste indica algo? (CSMR2013)

Em março, apresentei um pequeno pedaço da minha pesquisa de doutorado no CSMR2013, em Gênova. O CSMR é uma conferência voltada para trabalhos que discutem manutenção e evolução de software.

Meu trabalho discute a relação entre a quantidade de asserts e a qualidade do código de produção. Será que o desenvolvedor faz uso de mais de 1 assert por teste porque o código de produção é complexo? É mais ou menos essa a pergunta que o trabalho visa responder.

No fim, acabei encontrando que a quantidade de asserts unicamente não indica nada: os desenvolvedores usam um número arbitrário de asserts mesmo com o código sendo simples. Mas algo interessante emergiu: se ao invés de contar a quantidade de asserts, eu contar a quantidade de diferentes objetos que recebem uma asserção e fizer a mesma comparação, isso sim indica que o código de produção pode ser mais complicado do que a média.

Em código, pra ficar mais fácil de entender:

assertEquals(“esperado”, a.getAlgumaCoisa());
assertEquals(“esperado2″, a.getAlgumaOutraCoisa());

Veja que estou fazendo 2 asserts, mas sempre no mesmo objeto “a”. Agora:

assertEquals(“esperado”, a.getAlgumaCoisa());
assertEquals(“esperado2″, b.getAlgumaOutraCoisa());

Veja que tenho 2 asserts em 2 objetos diferentes: “a” e “b”. Nesse caso, os métodos “getAlgumaCoisa()” e “getAlgumaOutraCoisa()” podem estar mais complicados do que deveriam. Portanto, acabei propondo o mau cheiro de teste que é “Mais de Uma Instância de Objeto Recebendo Asserção”. Repare nesse padrão, pois ele pode indicar problemas no seu código de produção.

Você pode ler o paper completo aqui se preferir! :)

Agile Tour 2011 em Poços de Caldas

Esse mês tive o prazer de fazer a palestra de abertura do Agile Tour Poços de Caldas 2011.

A palestra foi bem introdutória. Discuti sobre testes automatizados e TDD. Minha ideia maior foi motivar os participantes a escreverem testes automatizados, a conhecerem TDD, e a perceberem que dá pra fazer isso de maneira divertida! 😉

Seguem meus slides!