Qualidade e JUnit: introduzindo automatização de testes unitários do seu software Java no dia-a-dia

Mariana Azevedo
6 min readOct 23, 2018

--

No dia-a-dia do profissional de software, os passos para escrever um código de alta qualidade são: dominar conceitos básicos e boas práticas de programação para deixar o código limpo legível e fácil de ser mantido; se cercar de ferramentas adequadas para avaliação e manutenção dessa qualidade durante e após o desenvolvimento do código; e, entender e dominar princípios de qualidade na aplicação de padrões em seus projetos. E sobre esses assuntos, já discutimos nos três textos que introduziram essa série sobre qualidade:

Entretanto, indo além de boas práticas e de uma boa arquitetura, você precisa ter uma estrutura bem organizada para que suas aplicações possam ser devidamente verificadas e testadas.

Code, without tests, is not clean. No matter how elegant it is, no matter how readable and accessible, if it hath not tests, it be unclean. — Uncle Bob Martin

O teste é uma das partes mais importantes para a garantia da qualidade no desenvolvimento de um software. Testar é assegurar que todas as funcionalidades esperadas estão implementadas corretamente. Testar é descobrir falhas/bugs para que sejam corrigidas até que a versão final atinja a qualidade desejada. Testar é validar se uma parte do software está pronta para ser disponibilizada em produção.

Os testes são feitos em várias etapas, sendo a primeira delas o Teste de Unidade. Testes de unidade são testes em um nível de componente ou classe, ou seja, cujo objetivo é um “pedaço do código”. Eles ajudam o desenvolvedor a verificar se a lógica de um programa está correta. A execução de testes ajuda automaticamente a identificar regressões de software introduzidas por alterações no código-fonte. Ter uma alta cobertura de teste do seu código permite que você continue desenvolvendo recursos sem ter que realizar muitos testes manuais.

Tá, mas como automatizar um teste de unidade e facilitar esse processo de garantia da qualidade do seu software Java no seu dia-a-dia? A resposta é: jUnit.

O que é o jUnit?

É um framework open-source para construção de testes automatizados em Java, hospedado no Github, em que se verifica as funcionalidades de classes e seus métodos. Além disso, podemos automatizar também a execução de todos os testes de forma que quando há uma nova versão estável do sistema, o framework execute todos os testes para garantir a integridade e estabilidade do que foi desenvolvido.

O jUnit funciona com base em anotações (Annotations) e essas anotações indicam se um método é de teste ou não, se um método deve ser executado antes da classe e/ou depois da classe. As anotações também indicam se o teste deve ou não ser ignorado e se a classe em questão é uma suíte de teste, ou seja, se a partir desta classe é disparada a execução das demais classes de teste, entre outras anotações menos utilizadas.

Toda classe de teste deve ser criada com a palavra Test no final. Por exemplo, DatabaseConnectionTest. Se você estiver usando uma ferramenta de automação de compilação, por exemplo, o Maven, a regra do sufixo Test para as classes de teste também vale. O Maven (através do plug-in Surfire) inclui automaticamente essas classes em seu escopo de teste. Já um teste é um método contido nessas classes que é usada apenas para teste e anotado com um @Test. Este método é a execução do código em teste.

Exemplo de classe e método de teste

Testando funcionalidades

Para testar seu código em condições específicas, utilizamos o método assert, fornecido pelo framework ou outra estrutura de declaração, normalmente chamadas de asserts ou assert statements. Essa estrutura permite que especifiquemos uma mensagem de erro e comparar o resultado real do teste com o resultado esperado. Temos vários tipos de asserts que podem ser implementados:

  • fail([message]): pode ser usado para verificar se uma determinada parte do código não é atingida ou para ter um teste com falha antes que o código de teste seja implementado. O parâmetro da mensagem é opcional.
  • assertTrue([message,] boolean condition): verifica se a condição booleana é verdadeira.
  • assertFalse([message,] boolean condition): verifica se a condição booleana é falsa.
  • assertEquals([message,] expected, actual): testa se dois valores (esperado e atual) são os mesmos. No caso de arrays, a verificação é em relação à referência e não ao conteúdo.
  • assertEquals([message,] expected, actual, tolerance): testa se dois valores float ou double correspondem. A tolerância é o número de casas decimais que devem ser consideradas na comparação.
  • assertNull([message,] object): verifica se o objeto é nulo.
  • assertNotNull([message,] object): verifica se o objeto não é nulo.
  • assertSame([message,] expected, actual): verifica se ambas as variáveis se referem ao mesmo objeto.
  • assertNotSame([message,] expected, actual): verifica se ambas as variáveis se referem a objetos diferentes.

Um exemplo de implementação pode ser observado no teste de quantidade de linhas de código da classe HelloWorld.

Classe HelloWorld

Nesse exemplo, a quantidade de linhas código dessa classe (sem contar comentários e espaços em branco) é 23. Ao implementar a classe de teste e invocar um método de medição, o assertEquals compara se o valor calculado pela medição é igual ao valor esperado (23).

Exemplo de um teste de medição da quantidade de linhas de código na classe HelloWorld

Para executar o teste e visualizar os resultados, basta clicar com o botão direito do mouse no projeto Java, cuja implementação do teste foi realizada e selecionar as opções Run as -> jUnit Test.

Exemplo de execução de testes em um projeto Java

Os resultados dos testes são exibidos na aba JUnit e podem ter 3 tipos de saídas: Errors, quando ocorreu um erro na execução do teste (uma exceção não tratada é lançada ou há erros no código); Failures, quando o teste é executado e falha, seja por um erro na lógica do código ou porque o teste pode estar mal escrito; e, Success, quando o teste passa. No exemplo abaixo, o teste da nossa classe LinesOfCodeTest foi bem-sucedido, ou seja, o valor encontrado na medição do código foi o esperado (23). Também podemos ver exemplos de testes com falhas e outros com erros no código.

Exemplo de exibição dos resultados dos testes na aba do jUnit

Uma outra funcionalidade importante do jUnit é a criação de uma suíte de testes. Uma suíte nada mais é do que uma classe Java onde todos os testes são iniciados, além de descrever quais são as classes de teste do projeto ou padrão dessas classes de teste para a sua execução.

No exemplo abaixo, foi criado uma suíte para gerenciar uma coleção de testes unitários, composta pelos seguintes testes: LinesOfCodeTest, NumberOfMethodsTest e NumberOfAttributesTest. Dessa forma, os testes serão executados na ordem que foram adicionados na suíte.

Exemplo de implementação de uma suíte de teste

Como vimos nos tópicos acima, introduzir testes unitários automatizados no seu dia-a-dia é uma tarefa simples e bem trivial. E por que fazer testes unitários? A resposta também é simples. Porque a implementação de testes unitários em seus projetos, permite solucionar dois problemas recorrentes que podem interferir na qualidade de um software: a alta taxa de defeitos, que são os erros frequentes de funcionamento do seu software; e, a deterioração do sistema, que são os problemas a longo prazo que inutilizar o software em um futuro.

Em resumo, só conseguimos descobrir falhas, defeitos e erros se fizermos testes. O teste unitário e a adoção de uma ferramenta muito reconhecida pelo mercado como o jUnit pode tornar esse processo mais rápido e, de certa forma, mais barato. Pense nisso!

Espero que tenham gostado do post. Abraços!

Referências
1. Código Limpo: Habilidades Práticas do Agile Software (Robert C. Martin, 2011)

2. Unit Testing with JUnit: http://www.vogella.com/tutorials/JUnit/article.html

--

--

Mariana Azevedo
Mariana Azevedo

Written by Mariana Azevedo

Senior Software Developer/Tech Lead, master in Computer Science/Software Engineering, Java, open source, and software quality enthusiast.

Responses (1)