Ir para o conteúdo principal
Background Image

Dicas para escrever bom testes automatizados

··5 minutos·
Rafael Issao
Autor
Rafael Issao
Apaixonado por tecnologia, programação e inovação. Adoro compartilhar conhecimento e aprender coisas novas todos os dias.
Tabela de conteúdos

Escrever testes não é uma tarefa fácil. Dificilmente aprendemos na faculdade e também não recebemos treinamento ou mentoria no trabalho.

Mas independente da linguagem, ferramentas e bibliotecas, algumas práticas que são a base para uma boa escrita de testes.

Neste artigo compartilho algumas destas dicas, em ordem de execução, para iniciar a jornada de uma boa escrita de testes!

Dados, Ação e Verificação
#

Todos os testes devem ter três partes claras para uma boa execução dos testes. Vamos ver um exemplo.

class TestClass {
    @Test
    void ehAnoBissextoTest() {
        // Dados
        int ano = 2016;

        // Ação
        boolean resultado = AnoBissexto.ehAnoBissexto(ano);

        // Verificação
        Assertions.assertTrue(resultado);
    }
}

Qualquer tipo de teste sempre terá estas três partes. Se faltar uma delas, provavelmente existe algo errado.

Os dados são necessários para a execução do teste.

Ação é a parte do seu software que você quer exercitar para verificar o resultado.

E a verificação existe para garantir que o comportamento esperado por você é o comportamento atual do seu software.

Se você sabe exatamente onde estão estas três partes, pode escrever de uma forma mais sucinta:

class TestClass {
    @Test
    void ehAnoBissextoTest() {
        // Dados + Ação + Verificação
        Assertions.assertTrue(AnoBissexto.ehAnoBissexto(2016));
    }
}

Então, toda vez que você começar a escrever um teste, comece simples sempre pensando nestas três partes.

Dê atenção a nomes de classe e dos métodos de teste
#

O segundo passo é começar a pensar em casos de testes.

Quando a função devolve true ou false?

Para deixar claro qual o caso de teste que estamos testando, aproveitamos o nome da classe e do método para especificar um cenário específico de teste.

O teste sempre deve verificar um comportamento específico do seu software.

Então dê nomes significativos e ubíquo para o nome das classes e métodos para um comportamento específico.

Um exemplo poderia ser:

class AnoBissextoSpec {

    @Nested
    class EhBissextoQuando {
        @Test
        void seEhDivisivelPor4MasNaoPor100() {}

        @Test
        void seEhDivisivelPor400() {}
    }

    @Nested
    class NaoEhBissextoQuando {
        @Test
        void seNaoEhDivisivelPor4() {}

        @Test
        void seEhDivisivelPor100MasNaoPor400() {}
    }
}

No exemplo acima, deixamos claro qual a responsabilidade de cada teste e qual o escopo que ele pertence.

Separamos por classe as situações positivas e negativas da função e os métodos deixam claro qual a especificação exata que estamos testando.

Aumente a amostra dos seus testes
#

Um bom design de teste permite adicionar mais dados para serem executados.

Talvez o teste passe para um valor mas talvez não passe para um específico valor.

Ou seja, quanto for maior a quantidade de dados, ou amostras para seu teste, mais confiável o comportamento atual do seu software.

Um exemplo poderia ser assim:

class AnoBissextoSpec {

    @Nested
    class EhBissextoQuando {
        @ParameterizedTest
        @ValueSource(ints = {320, 240, 1280, 2160})
        void seEhDivisivelPor4MasNaoPor100(int ano) {
            System.out.println(ano);
        }

        @ParameterizedTest
        @ValueSource(ints = {400, 800, 1600, 2400})
        void seEhDivisivelPor400(int ano) {
            System.out.println(ano);
        }
    }

    @Nested
    class NaoEhBissextoQuando {
        @ParameterizedTest
        @ValueSource(ints = {2018, 2017, 47, 1})
        void seNaoEhDivisivelPor4(int ano) {
            System.out.println(ano);
        }

        @ParameterizedTest
        @ValueSource(ints = {1700, 1800, 300, 100})
        void seEhDivisivelPor100MasNaoPor400(int ano) {
            System.out.println(ano);
        }
    }
}

A ideia deste passo é aumentar a quantidade de dados que utilizamos para exercer o teste.

Serve também como exemplo de quais anos se encaixam em cada especificação.

Alguns frameworks e biblioteca de testes chamam esta técnica de Data-Driven-Testing.

Pense em casos negativos e de exceção
#

Quais os cenários que o seu código não suporta? E o que acontece quando ocorre este cenário?

Então crie testes e cenários para estes casos também.

Um exemplo:

class AnoBissextoSpec {

    @Nested
    class EhBissextoQuando {
        @ParameterizedTest
        @ValueSource(ints = {320, 240, 1280, 2160})
        void seEhDivisivelPor4MasNaoPor100(int ano) {
            System.out.println(ano);
        }

        @ParameterizedTest
        @ValueSource(ints = {400, 800, 1600, 2400})
        void seEhDivisivelPor400(int ano) {
            System.out.println(ano);
        }
    }

    @Nested
    class NaoEhBissextoQuando {
        @ParameterizedTest
        @ValueSource(ints = {2018, 2017, 47, 1})
        void seNaoEhDivisivelPor4(int ano) {
            System.out.println(ano);
        }

        @ParameterizedTest
        @ValueSource(ints = {1700, 1800, 300, 100})
        void seEhDivisivelPor100MasNaoPor400(int ano) {
            System.out.println(ano);
        }
    }

    @Nested
    class NaoSuportaELancaExcecao {
        @Test
        void quando0AnoEhZero() {}

        @ParameterizedTest
        @ValueSource(ints = {-1, -100, -400})
        void quando0AnoEhNegativo(int ano) {
            System.out.println(ano);
        }
    }
}

É assim que encontramos bugs mais cedo.

Desta forma conseguimos trabalhar de forma preventiva.

Sempre verifique que o seu teste falha
#

Um dos passos mais importantes e mais ignorado na escrita de um teste é a verificação de que ele falha quando você muda o código de produção.

Isso acontece principalmente quando o código de teste é escrito depois de um código de produção.

Não adianta seguir todas as dicas acima se o seu teste nunca falha. É um falso positivo.

Então, para cada teste novo, sempre verifique que ele falha.

Apague uma soma, mude o sinal de “>” para “<”, comente uma linha.

É a dica mais importante deste artigo!

Conclusão
#

Para escrever um bom teste não adianta fazê-lo passar.

Um bom teste deve ter as seguintes características:

  • As três partes claras: Dados, Ação e Verificação
  • Uma boa amostragem para o teste
  • Especificação clara que está sendo testada
  • Casos negativos e de exceção
  • O teste falha quando mudamos o código de produção

Com estas dicas, você poderá iniciar a jornada de uma boa escrita de testes.

Estas dicas podem ser utilizadas em qualquer linguagem, ferramenta.

E é possível aplicar em testes unitários, integração, contrato, API e etc.

Se você tiver dúvidas, sugestões ou até correções, sinta-se à vontade para comentar ou falar diretamente comigo!

🥒🥒 Até o próximo artigo! 🥒🥒