Melhores Práticas com JUnit

Olá Pessoal,

    Recentemente desenvolvi um trabalho bem legal na atual empresa que trabalho relacionado a testes unitários, de um modo geral acredito que assim como aconteceu comigo, o mais natural ainda é ter na empresa diversos softwares que foram construídos na base do “vai ou racha”, ou seja, sem o mínimo de cobertura de testes possível. Dica-se de passagem existe até um sistema que trabalho hoje que possui testes unitários, mas que deixaram de funcionar e hoje servem apenas como empecilho na hora de executar o build do Maven, ou seja, sempre tenho que executar o comando abaixo :

     mvn install -DskipTests

   Claro que isso está completamente errado, acho até que o desenvolvedor inocente e recém graduado tinha uma esperança singela no fundo do coração de que isso iria servir de verdade um dia, o pior é é que gestores despreparados e sem o pingo de noção sobre a importância dos testes unitários acabaram dando mais valor a entrega do que a garantia da qualidade em si, o resultado disso hoje é que os testes unitários mais atrapalham do que ajudam.

    Meu querido amigo e leitor deste humilde blog, gostaria de dizer que ainda existe esperança no fim do túnel, mas exige preparo e paciência. A primeira coisa que devemos ter em mente é que testes unitários podem existir sozinhos, mas que ficam muito mais belos quando estão sendo utilizados pela empresa como uma ferramenta que faz parte da metodologia TDD, Test Driven Development, ou estão sendo utilizados como parte de um ciclo bem estruturado de ALM, Application Lifecycle Management. Sem querer prolongar muito o texto que hoje estou escrevendo, recomendo fortemente aos interessados a leitura do livro abaixo, escrito por Martin Fowler e que é tido por muitos como uma obra neste tipo de assunto.   

   O que de fato gostaria de passar neste post hoje é a importância dos testes unitários e como devemos construí-los, separei então algumas dicas que podem ajudar os desenvolvedores a construir testes uteis e que com certeza terão uma vida longa no build do projeto.

1.  Não realizar o SetUp do teste no construtor

    Jamais realize o set up do caso de teste no construtor, além de dificultar o tratamento de exceção do framework, você não estaria utilizando um método que foi criado especificamente para isso. Ao invés, considere sobrescrever o método  setUp().

Exemplo:

@Override
protected void setUp() throws Exception {
    super.setUp();
    //Realizar o setUp aqui…..
}

2.  Não depender da ordem de execução dos testes

    Evite construir os testes unitários pensando na ordem que eles devem ser executados, pois além de dificultar a inclusão de novos testes, com o tempo e sem o devido cuidado pode acarretar em um teste unitário sem uso prático Outro ponto importante é que a execução dos testes não é garantida pelo JUnit, além do fato de que pode variar bastante dependendo do ambiente e da JVMs que estiver sendo utilizada. Contudo, se a ordem for algo realmente importante para o teste em questão, você deve construir um método estático para forçar a ordem, semelhante ao exemplo abaixo:

Exemplo:

public static Test suite() {
   suite.addTest(new SomeTestCase (“testDoThisFirst”;));
   suite.addTest(new SomeTestCase (“testDoThisSecond”;));
   return suite;
}

3.  Cuidado com os Efeitos Colaterais

    Muito embora a construção de um teste unitário seja um processo autocontido, ou seja, sem depender de nenhum outro método ou recurso externo, é comum a construção de métodos que acabam por modificar uma variável global ou parâmetro de uso geral que tende a quebrar os testes atuais. Dessa forma, cabe sempre que o desenvolvedor estiver construindo um teste unitário realizar uma análise prévia em torno dos testes existentes para entendimento e com intenção de evitar a inclusão de novos componentes que irão afetar os já existentes.

4.   Chamar os métodos SetUp() e TearDown

    Além de ser perfeitamente comum, é recomendável que quando se estiver projetando os testes unitários seja feito uso de herança, a fim de evitar repetição de código fonte e garantir o reaproveitamento de funcionalidades. Não se esquecendo de chamar os métodos setUp() e TearDown() para fazer a configuração inicial e o fechamento de recursos, respectivamente.

5.    Não usar Hard-Coded

    Como dito anteriormente, o teste unitário deve ser autocontido e sem depender de qualquer recurso externo, mas quando isso for impreterivelmente necessário, deve-se evitar hard code, pois isso impede que este teste fique portável em diferentes ambientes. Imagine, por exemplo, o código abaixo que tenta carregar o arquivo data1.dat diretamente da unidade C do sistema operacional, pode-se imaginar que o criador deste código estava utilizando o sistema operacional Windows, mas o que aconteceria se por um acaso o desenvolvedor tentasse rodar este código no Linux, provavelmente não iria rodar! Dito isto, é importante lembrar-se de sempre usar caminhos relativos quando se estiver referenciando algum tipo de recurso, vide exemplos a seguir.

Exemplo de código errado:

    public void setUp ()  {      
     FileInputStream inp = new FileInputStream(“C:\\TestData\\data1.dat”);
}

Exemplo de código correto:

     public void setUp ()  {      
    FileInputStream inp = new FileInputStream(“data1.dat”);
}

6. Nomear os testes unitários cuidadosamente

    Outro ponto importante trata-se dos nomes dos testes unitários, nomeie-os com cautela e bom senso. Lembre-se de que este código pode ser lido no futuro por um programador de outra equipe, ou ainda pelo próprio criador anos mais tarde. Por exemplo, o nome do caso de teste que irá testar a classe MessageLog, deveria ser TestMessageLog, já o nome dos métodos deve sempre representar a real intenção daquele teste, nomes validos são:

    testLoggingEmptyMessage()
    testLoggingNullMessage()
    testLoggingWarningMessage()
    testLoggingErrorMessage()

7.   Não Incluir informação temporal

    Quando estiver construindo um teste unitário, deve-se a qualquer custo evitar colocar informação temporal, fazendo com que o teste unitário dependa de uma data especifica para sua execução ou ainda pare de funcionar por conta disso.

8.    Utilize Locale

    Como dito anteriormente, a execução dos testes unitários pode ocorrer em diferentes ambientes e sistemas operacionais, dessa forma quando se estiver trabalhando com informações que podem variar de região para região, como uma data, deve-se fazer uso de componentes internacionalizáveis. Conforme exemplo abaixo:

Uso incorreto:

Date date = DateFormat.getInstance ().parse (“dd/mm/yyyy”);

Uso correto:

Calendar cal = Calendar.getInstance ();
Cal.set (yyyy, mm-1, dd);
Date date = Calendar.getTime ();

9.   Utilizar JavaDoc

    Deve-se impreterivelmente colocar comentários do tipo JavaDoc logo acima da assinatura do método de teste, com intuito de ajudar nas futuras manutenções do código fonte.  Conforme exemplo abaixo:

/**
 * Teste unitário que verifica as quatro operações básicas
 * da calculadora criada.
 **/
@Test
public void testOperacoesBasicas() {

       // MyClass is tested
       MyCalc tester = new MyCalc();

       // Tests
       assertEquals(“10 x 0 must be 0”, 0, tester.multiply(10, 0));
       assertEquals(“0 x 10 must be 0”, 0, tester.multiply(0, 10));
       assertEquals(“0 x 0 must be 0”, 0, tester.multiply(0, 0));
}

Bem pessoal, espero que tenham gostado dessas pequenas dicas.

Um grande abraço.

Att.
Natanael Fonseca

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s