Você conhece testes parametrizados?

Versão em Inglês

É um comportamento comum refatorar o código principal da aplicação para deixar mais legível durante o desenvolvimento do software. Mas e códigos de testes você tem o mesmo cuidado?

Neste artigo, eu quero mostrar uma forma de escrever testes reduzindo o número de linhas e assim sendo mais produtivo fazendo o uso de testes parametrizados.

Os exemplos a seguir serão com Junit5. Para o funcionamento adequado é necessário a instalar a depêndencia junit-jupiter-params.

Criei um simples código onde o objetivo é apenas executar operações matemáticas com dois números.

public class Calculator {

   private int x;
   private int y;

   private String operation;

   public Calculator (int x, int y, String operation) {
       this.x = x;
       this.y = y;
       this.operation = operation;
   }

   public int calculate() {

       switch (this.operation) {

           case "add":
               return x + y;

           case "subtract":
               return x - y;

           case "multiply":
               return x * y;

           case "divide":
               return x / y;

           default:
               return 0;
       }
   }
}

O exemplo acima não tem mistério. Recebe x, y e de acordo com a operação o resultado será retornado. Se a operação é de desconhecida, o resutado será zero.

Testar o código sem testes parametrizados geraria pelo menos cinco métodos e um monte de linhas como estas:

public class CalculatorTest {

   @Test
   void mustTestAddition() {

       // given
       Calculator calculator = new Calculator(10, 2, "add");

       // when
       int result = calculator.calculate();

       // then
       Assertions.assertEquals(result, 12;
   }

   @Test
   void mustTestSubtraction() {

       // given
       Calculator calculator = new Calculator(10, 2, "subtract");

       // when
       int result = calculator.calculate();

       // then
       Assertions.assertEquals(result, 8);
   }

   @Test
   void mustTestMultiplication() {

       // given
       Calculator calculator = new Calculator(10, 2, "multiply");

       // when
       int result = calculator.calculate();

       // then
       Assertions.assertEquals(result, 20);
   }

   @Test
   void mustTestDivision() {

       // given
       Calculator calculator = new Calculator(10, 2, "divide");

       // when
       int result = calculator.calculate();

       // then
       Assertions.assertEquals(result, 5);
   }

   @Test
   void mustTestInvalidOperation() {

       // given
       Calculator calculator = new Calculator(10, 2, "any");

       // when
       int result = calculator.calculate();

       // then
       Assertions.assertEquals(result, 0);
   }
}

Resumindo seria um teste para cada operação. Agora veja como fica quando usamos testes parametrizados.

public class CalculatorTest {
   
   @ParameterizedTest
   @CsvSource({
       "10,2,add,12",
       "10,2,subtract,8",
       "10,2,multiply,20",
       "10,2,divide,5",
       "10,2,any,0"
   })
   void mustTestCalculatorOperations(int x, int y, String operation, int expectedResult) {

       // given
       Calculator calculator = new Calculator(x, y, operation);

       // when
       int result = calculator.calculate();

       // then
       Assertions.assertEquals(result, expectedResult);
   }
}

É só adicionar a anotação @ParameterizedTest, definir os cenários dentro de @CsvSource seguindo a sequência de parâmetros que está declarada no método.

Testes parametrizados após a executar na IDE IntelliJ

Pessoa mostrando a parte interna das mãos como um gesto para mostrar que algo é fácil

Apesar de ser um texto simples, eu acho que o objetivo foi cumprido. Mas os testes parametrizados tem várias formas de serem executados. Se você quiser se aprofundar mais, eu realmente recomendo o Guia de testes automatizados no Junit5.

Testes parametrizados Java Junit

Discussion and feedback