Publicado em

- 4 min read

Testes unitários com @ParameterizedTest no JUnit 5: Escrevendo menos, testando mais

img of Testes unitários com @ParameterizedTest no JUnit 5: Escrevendo menos, testando mais

Introdução

“Se você repete código nos testes, está fazendo errado.”
Essa frase pode soar provocativa, mas é exatamente o que acontece quando ignoramos os testes parametrizados no JUnit 5.

Quem já escreveu testes unitários em Java sabe como é comum cair na repetição: testamos valores diferentes, mas o formato do teste é sempre o mesmo. Isso gera mais linhas de código, torna a manutenção cansativa e, muitas vezes, desanima a escrever novos testes.

A boa notícia é que o JUnit 5 oferece uma forma simples e elegante de resolver esse problema: os testes parametrizados. Com o uso de anotações como @ParameterizedTest, @ValueSource, @NullSource, @EmptySource, @EnumSource, é possível escrever menos código, testar mais cenários e deixar tudo mais limpo, expressivo e poderoso.

Neste artigo, vamos aprender na prática como essas anotações funcionam e como podem transformar seus testes em ferramentas muito mais produtivas no dia a dia.

Setup

Utilizaremos as dependências abaixo:

   <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.13.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-params</artifactId>
    <version>5.13.4</version>
    <scope>test</scope>
</dependency>

Porque utilizar testes parametrizados?

Imagine que você precisa validar se uma string não está vazia. A forma tradicional seria escrever vários testes:

   @Test
void deveValidarStringComConteudo() {
    assertTrue(StringUtils.isNotBlank("Java"));
}

@Test
void deveValidarStringComEspacos() {
    assertFalse(StringUtils.isNotBlank("   "));
}

@Test
void deveValidarStringVazia() {
    assertFalse(StringUtils.isNotBlank(""));
}

Funciona? Sim. Mas é verboso e difícil de manter.
Com testes parametrizados, escrevemos um único método que roda com vários valores.

Começando com @ParameterizedTest e @ValueSource

A maneira como criamos os testes continua a mesma, mas inserindo anotação @ParameterizedTest, informamos para o teste que daremos algumas entradas para que ele utilize a mesma estrutura, executando uma vez para cada uma.

Os valores informados através das anotações de origem (source), serão passadas através do argumento do método para cada execução.

A anotação @ValueSource permite informar valores primitivos e strings:

   @ParameterizedTest
@ValueSource(strings = {"Java", "JUnit", "Spring"})
void deveValidarStringsNaoVazias(String palavra) {
    assertTrue(palavra.length() > 0);
}

Aqui, o JUnit executa o mesmo teste para cada valor. Três testes, um método. Bem melhor, não?

Execução do teste
Execução do teste

Incluindo valores nulos e vazios: @NullSource e @EmptySource

Quer testar cenários com null e strings vazias? Simples!

A anotação @NullSource passa nulo para o argumento do método, enquanto @EmptySource passa uma String vazia:

   @ParameterizedTest
@NullSource
@EmptySource
@ValueSource(strings = {" ", "\t"})
void deveRetornarFalsoParaStringsInvalidas(String texto) {
    assertFalse(StringUtils.isNotBlank(texto));
}

Com isso, cobrimos:

  • null
  • String vazia ""
  • Strings com espaço ou tabulação

Um único teste cobre vários casos críticos!

Testando Enums com @EnumSource

Enums são comuns em validações de negócio.
Considerando que temos o enum:

   enum Status {
  ATIVO, INATIVO, PENDENTE
}

Quer validar que todos os status não são nulos?

   @ParameterizedTest
@EnumSource(Status.class)
void deveValidarStatusNaoNulo(Status status) {
  assertNotNull(status);
}

E se quiser filtrar? Ex.: apenas ATIVO e INATIVO:

   @ParameterizedTest
@EnumSource(value = Status.class, names = { "ATIVO", "INATIVO"})
void deveValidarApenasStatusPermitidos(Status status) {
  assertTrue(status == Status.ATIVO || status == Status.INATIVO);
}

Quando os dados são complexos: @MethodSource

Para cenários mais elaborados, como múltiplos parâmetros, usamos @MethodSource. Essa anotação permite informar o nome do método que irá fornecer as entradas para realização dos testes, perceba que nesse cenário, temos mais de um argumento no método:

   @ParameterizedTest
@MethodSource("fornecerNumerosParaSoma")
void deveSomarNumerosCorretamente(int a, int b, int esperado) {
  assertEquals(esperado, a + b);
}

static Stream < Arguments > fornecerNumerosParaSoma() {
  return Stream.of(
    Arguments.of(1, 2, 3),
    Arguments.of(10, 5, 15),
    Arguments.of(-1, 1, 0)
  );
}

Agora temos cenários dinâmicos e expressivos, sem duplicação.

Dicas para testes parametrizados de qualidade

  • Use nomes de métodos descritivos;
  • Cuidado com cenários excessivos (evite datasets gigantes);
  • Combine @NullSource e @EmptySource para inputs frágeis;
  • Prefira @MethodSource para dados complexos ou dependentes;

Conclusão

Escrever testes unitários não precisa ser sinônimo de repetição e código verboso. Como vimos, os testes parametrizados do JUnit 5 são uma forma prática e elegante de simplificar esse processo.

Com anotações como @ValueSource, @NullSource, @EmptySource, @EnumSource, conseguimos validar vários cenários de forma clara, reduzindo a duplicação e aumentando a cobertura dos testes.

No fim das contas, vale lembrar da frase que guiou este artigo: “Se você repete código nos testes, está fazendo errado.”

Ao adotar os testes parametrizados, você ganha produtividade, clareza e confiança no seu código — três elementos fundamentais para qualquer desenvolvedor que busca qualidade e eficiência no dia a dia.

👉 Agora é com você: que tal aplicar um @ParameterizedTest no seu próximo teste e sentir na prática essa diferença? E se este conteúdo te ajudou, compartilhe com sua equipe para espalhar boas práticas no desenvolvimento.

🔗 Repositório com projeto completo

Acesse o repositório com o projeto completo pronto para rodar:
👉 https://github.com/LuizEduardoBilotta/demo—lebilotta-parameterizedTest

📚 Referências oficiais