Tag Archives: tdd

Is TDD magic?

Will I create a better class design if I use Test-Driven Development? Well, yes and no; TDD doesn’t do magic. In this article, we discuss the effects of the practice, and how the practice can help developers during class design. We do it by interviewing developers from different companies.

Developers commonly argue that TDD helps software design, improving internal code quality. For example, Kent Beck [4], Robert Martin [5], Steve Freeman [6], and Dave Astels [7], state in their books (without scientific evidence) that the writing of unit tests in a TDD fashion promotes significant improvement in class design, helping developers to create simpler, more cohesive, and less coupled classes. They even consider TDD as a class design technique [8], [9]. Nevertheless, the way in which TDD actually guides developers during class design is not yet clear.

We ask: What are the developers’ perceptions on the effects of Test-Driven Development in class design?

We invited developers from the Brazilian software development industry to be part of this study. We had 14 participants from 6 different companies.

TDD does not drive to a better design by itself

Participants affirmed that the practice of TDD did not change their class design during the experiment. The main justification was that their experience and previous knowledge regarding object-orientation and design principles guided them during class design. They also affirmed that a developer with no knowledge in object-oriented design would not create a good class design just by practicing TDD or writing unit tests.

The participants gave two good examples reinforcing the point. One of them said that he made use of a design pattern [30] he learned a few days before. Another participant mentioned that his studies on SOLID principles helped him during the exercises.

The only participant who had never practiced TDD before stated that he did not feel any improvement in the class design when practicing the technique. Curiously, this participant said that he considered TDD a design technique. It somehow indicates that the popularity of the effects of TDD in class design is high. That opinion was slightly different from that of experienced participants, who affirmed that TDD was not only about design, but also about testing.

However, different from the idea that TDD and unit tests do not guide developers directly to a good class design, all participants said that TDD has positive effects on class design. Many of them mentioned the difficulty of trying to stop using TDD or thinking about tests, what can be one reason for not having significant difference in terms of design quality in the code produced with and without TDD.

“When you are about to implement something, you end up thinking about the tests that you’ll do. It is hard to think “write code without thinking about tests!”. As soon as you get used to it, you just don’t know another way to write code… ”

According to them, TDD can help during class design process, but in order to achieve that, the developer should have certain experience in software development. Most participants affirmed that their class designs were based on their experiences and past learning processes. In their opinion, the best option is to link the practice of TDD and experience.

Baby steps and simplicity

TDD suggests developers to work in small (baby) steps; one should define the smallest possible functionality, write the simplest code that makes the test green, and do one refactoring at a time [4].

In the interviews, participants commented about this. One of them mentioned that, when not writing tests, a developer thinks about the class design at once, creating a more complex structure than needed.

One of the participants clearly stated how he makes use of baby steps, and how it helps him think better about his class design:

“Because we start to think of the small and not the whole. When I do TDD, I write a simple rule (…), and then I write the method. If the test passes, it passes! As you go step by step, the architecture gets nice. (…) I used to think about the whole (…). I think our brain works better when you think small. If you think big, it is clear, at least for me, that you will end up forgetting something. ”

Refactoring confidence

Participants affirmed that, during the process of class design, changing minds is constant. After all, there is still a small knowledge about the problem, and about how the class should be built. This was the most mentioned point by the participants. According to them, an intrinsic advantage of TDD is the generated test suite. It allows developers to change their minds and refactor all the class design safely. Confidence, according to them, is an important factor when changing class design or even implementation.

“It gives me the opportunity to learn along the way and make things differently. (…). The test gives you confidence. ”

A participant even mentioned a real experience, in which TDD made the difference. According to him, he changed his mind about the class design many times and trusted the test suite to guarantee the expected behavior.

A safe space to think

In an analogy done by one of the participants, tests are like draft paper, in which they can try different approaches and change their minds about it frequently. According to them, when starting by the test, developers are, for the first time, using their own class. It makes developers look for a better and clearer way to invoke the class’ behaviors, and facilitate its use:

“Tests help you on that. They are a draft for you to try to model it the best way you can. If you had to model the class only once, it is like if you have only one chance. Or if you make it wrong, fixing it would give you a lot of work. The idea of you having tests and thinking about them, it is like if you have an empty draft sheet, in which you can put and remove stuff because that stuff doesn’t even exist.”

We asked their reasons for not thinking on the class design even when they were not practicing TDD or writing tests. According to them, when a developer does not practice TDD, they get too focused on the code they are writing, and thus, they end up not thinking about the class design they were creating. They believe tests make them think of how the class being created will interact with the other classes and of the easiness of using it.

One of the participants was even more precise in his statement. According to him, developers that do not practice TDD, as they do not think about the class design they are building, they end up not doing good use of OOP. TDD forces developers to speed down, allowing them to think better about what they are doing.

Rapid feedback

Participants also commented that one difference they perceived when they practiced TDD was the constant feedback. In traditional testing, the time between the production code writing and test code writing is too long. When practicing TDD, developers are forced to write the test first, and thus receive the feedback a test can provide sooner.

One participant commented that, from the test, developers observe and criticize the code they are designing. As the tests are done constantly, developers end up continuously thinking about the code and its quality:

“When you write the test, you soon perceive what you don’t like in it (…). You don’t perceive that until you start using tests.”

Reducing the time between the code writing and test writing also helps developers to create code that effectively solves the problem. According to the participants, in traditional testing, developers write too much code before actually knowing if it works.

The search for testability

Maybe the main reason for the practice of TDD helping developers in their class design is the constant search for testability. It is possible to infer that, when starting to produce code by its test, the production code should be, necessarily, testable.

When it is not easy to test a specific piece of code, developers understand it as a class design smell. When this happens, developers usually try to refactor the code to make it easier to test. A participant also affirmed that he takes it as a rule; if it is hard to test, then the code may be improved.

Feathers [33] raised this point: the harder it is to write the test, the higher the chance of a class design problem. According to him, there is a strong synergy between a highly testable class and a good class design; if developers are looking for testability, they end up creating good class design; if they are looking for good class design, they end up writing testable code.

Participants went even further. During the interviews, many of them mentioned patterns that made (and make) them think about possible design problems in the class they build. As an example, they told us that when they feel the need to write many different unit tests to a single method, this may be a sign of a non-cohesive method. They also said that when a developer feels the need to write a big scenario for a single class or method, it is possible to infer that this need emerges in classes dealing with too many objects or containing too many responsibilities, and thus, it is not cohesive. They also mentioned how they detect coupling issues. According to them, the abusive use of mocking objects indicates that the class under testing has coupling issues.

What did we learn from it?

The first interesting myth contested by the participants was the idea that the practice of TDD would drive developers towards a better design by itself. As they explained, the previous experience and knowledge in good design is what makes the difference; however, TDD helps developers by giving feedback by means of the unit tests that they are writing constantly. As they also mentioned, the search for testability also makes them rethink about the class design many times during the day — if a class is not easy to be tested, then they refactor it.

We agree with the rationale. In fact, when comparing to test-last approaches, developers do not have the constant feedback or the need to write testable code. They will have the same feedback only at the end, when all the production code is already written. That may be too late (or expensive) to make a big refactoring in the class design.

tdd-feedback

We also agree with the confidence when refactoring. As TDD forces developers to write unit tests frequently, those tests end up working as a safety net. Any broken change in the code is quickly identified. This safety net makes developers more confident to try and experiment new design changes — after all, if the changes break anything, tests will warn developers about it. That is why they also believe the tests are a safe space to think.

Therefore, we believe that is is not the practice by itself that helps developers to improve their class design; but it is the consequences of the constant act of writing a unit test, make that class testable, and refactor the code, that drives developers through a better design.

Conclusions

To answer the research question: What are the developers’ perceptions on the effects of Test-Driven Development in class design?

Developers believe that the practice of test-driven development helps them to improve their class design, as the constant need of writing a unit test for each piece of the software forces them to create testable classes. These small feedbacks — is your test easy to be tested or not? — makes them think and rethink about the class and improve it. Also, as the number of tests grow, they act as a safety net, allowing developers to refactor freely, and also try and experiment different approaches to that code. Based on that, we suggest developers to experiment the practice of test-driven development, as its effects look positive to software developers.

If you wanna read the full journal paper or need the references: http://www.journal-bcs.com/content/21/1/15.

Testes e infraestrutura: inimigos de longa data

TL;DR: Suas classes com regras de negócio não podem depender de infraestrutura. Elas devem ser facilmente testáveis por meio de testes de unidade. Dependências devem ter interfaces amigáveis e serem facilmente mockadas.

Às vezes ouço perguntas como “como testo meu sistema em Android”, ou “como testo meu sistema em JSF”, ou “como testo meu sistema em <ponha-sua-tecnologia-aqui>”. Pra mim, essa pergunta faz todo sentido se você está procurando uma maneira de fazer testes de sistema. Algo como Selenium, só que Android. No entanto, se você está pensando em testes de unidade, essa pergunta não deveria ser muito importante.

Regras de negócio são importantes. Tão importantes que merecem ficar o mais afastado possível da sua infraestrutura. Ou seja, não importa se você usa JSF, Android, ou whatever, o coração do sistema deve ficar em classes Java, simples, que não dependem de nada externo. Se você pensar sempre assim, pronto, todas suas regras de negócio serão facilmente testadas.

Ah, mas e se pra realizar aquela regra de negócios, eu precisar consultar uma infraestrutura? Sei lá, pegar um dado do banco de dados, ou postar algo em um serviço web? Faça sua classe depender de uma classe que tenha uma interface bastante amigável. Algo como bd.pegaValor() ou correios.calcula(“SP”). Nada que você tenha que ficar escrevendo linhas e linhas para chegar no resultado que espera. Assim, fica fácil mockar depois para testar. E dentro da dependência, você faz o que tiver que fazer pra fazer a infraestrutura funcionar.

Se você pensar assim sempre, a infraestrutura que está usando deixa de ser importante. Afinal, você fugiu dela. E testou o que precisava ser testado.

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! 🙂