W3docs

Testes Parametrizados JUnit em Java

Execute o mesmo teste JUnit com diferentes entradas usando @ParameterizedTest e fontes de valores.

Um teste parametrizado executa o mesmo método de teste várias vezes, uma vez para cada conjunto de entradas fornecido. Em vez de copiar e colar testReverseAbc, testReverseEmpty e testReverseSingle, você escreve a lógica uma vez e fornece uma fonte de dados — uma lista de entradas e resultados esperados. O JUnit 5 (motor Jupiter) torna isso de primeira classe com @ParameterizedTest e uma família de anotações de fonte. O benefício são menos linhas, maior cobertura e cada entrada reportada como seu próprio aprovado/reprovado.

Este capítulo assume que você já sabe como escrever e verificar um teste simples; caso contrário, comece pela introdução ao JUnit e pelas asserções JUnit. Ele aborda quando usar um teste parametrizado, como escolher uma fonte de argumentos (@ValueSource, @CsvSource, @MethodSource e outras), e o erro mais comum — um valor esperado errado em vez de um bug no código.

De testes repetidos a um único teste parametrizado

Um método @Test simples testa exatamente um cenário. Quando você quer verificar o mesmo comportamento em uma tabela de entradas, a abordagem ingênua repete o método:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;

class PrimesTest {
  @Test void two_isPrime()   { assertTrue(Primes.isPrime(2)); }
  @Test void seven_isPrime() { assertTrue(Primes.isPrime(7)); }
  @Test void thirteen_isPrime() { assertTrue(Primes.isPrime(13)); }
}

A versão parametrizada condensa os três em um único método. Você anota com @ParameterizedTest (não @Test) e anexa uma fonte que fornece o argumento para cada execução:

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.assertTrue;

class PrimesTest {
  @ParameterizedTest
  @ValueSource(ints = {2, 7, 13})
  void isPrime(int candidate) {
    assertTrue(Primes.isPrime(candidate));
  }
}

O JUnit invoca isPrime três vezes — candidate=2, depois 7, depois 13 — e reporta três resultados. Um valor que falha não oculta os demais.

Escolhendo uma fonte de argumentos

A anotação @ParameterizedTest é inútil sozinha; ela precisa de uma fonte que produza os argumentos. O JUnit Jupiter oferece várias, cada uma adequada a um formato diferente de dados:

FonteForneceMelhor para
@ValueSourceUm único literal por execução (ints, strings, doubles, …)Testes com um argumento
@CsvSourceUma linha de valores separados por vírgula por execuçãoAlgumas linhas inline com múltiplas colunas
@CsvFileSourceLinhas lidas de um arquivo .csv no classpathTabelas grandes ou mantidas externamente
@MethodSourceO que um método fábrica retornar como Stream/CollectionObjetos complexos, casos computados
@EnumSourceAs constantes de um enumCobertura exaustiva de um enum
@NullSource / @EmptySourceValores null e vaziosCobertura de casos extremos em strings/coleções

A regra geral: @ValueSource para uma entrada simples, @CsvSource para uma pequena tabela com múltiplas colunas, e @MethodSource quando os dados deixam de caber em literais de anotação.

Múltiplas colunas com @CsvSource

Quando cada caso tem uma entrada e uma saída esperada, @CsvSource oferece uma pequena tabela inline. Cada string é uma linha; vírgulas a dividem nos parâmetros do método em ordem:

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.assertEquals;

class StringsTest {
  @ParameterizedTest
  @CsvSource({
      "abc,     cba",
      "racecar, racecar",
      "'',      ''"          // single quotes denote an empty string
  })
  void reverse(String input, String expected) {
    assertEquals(expected, Strings.reverse(input));
  }
}

O JUnit converte cada token separado por vírgula para o tipo de parâmetro declarado, então @CsvSource({"4, 16"}) pode cair em (int n, int square). Use aspas simples para incluir vírgulas ou strings vazias dentro de uma célula.

Casos computados com @MethodSource

Valores de anotação devem ser constantes em tempo de compilação, então quando os argumentos são objetos reais ou precisam de computação, mude para @MethodSource. Ele nomeia um método estático que retorna um Stream<Arguments> (ou qualquer Collection/array):

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;

class TaxTest {
  static Stream<Arguments> brackets() {
    return Stream.of(
        Arguments.of(0,      0.0),
        Arguments.of(10_000, 1_000.0),
        Arguments.of(50_000, 7_500.0)
    );
  }

  @ParameterizedTest(name = "income {0} -> tax {1}")
  @MethodSource("brackets")
  void computesTax(int income, double expectedTax) {
    assertEquals(expectedTax, Tax.of(income));
  }
}

O atributo opcional name personaliza como cada invocação aparece no relatório de testes, com {0}, {1} representando os argumentos — indispensável quando uma única linha com falha precisa ser identificada rapidamente.

Um exemplo prático: um executor parametrizado sem JUnit

O executor de código não tem JUnit no classpath, portanto este programa modela o mecanismo que um teste parametrizado incorpora com código JDK simples: uma única verificação é definida uma vez e então executada sobre uma lista de casos — exatamente o que @ParameterizedTest faz por trás das anotações. Um caso está errado de propósito para que você possa ver como linhas isoladas passam ou falham.

java— editable, runs on the server

O que extrair da execução:

  • O bloco reverse imprime quatro linhas PASS e >> reverse: 4 passed, 0 failed — um único corpo (reverse) executado contra quatro linhas, espelhando como um único método @ParameterizedTest é invocado uma vez por linha de @CsvSource.
  • O bloco isPrime imprime PASS para as entradas 2, 7, 9 e 1, mas FAIL para a entrada 4, porque isPrime(4) retorna false enquanto a linha afirmava true — uma expectativa errada, não um bug no código, que é o erro mais comum em testes parametrizados.
  • Essa falha única é reportada em sua própria linha e contada como >> isPrime: 4 passed, 1 failed; as demais linhas ainda passam, demonstrando a principal vantagem sobre um loop manual com uma única asserção — cada entrada é um caso independente, reportado individualmente.
  • O auxiliar runAll recebe a unidade como Function e os casos como List, separando a lógica sob teste dos dados — exatamente a separação que @ParameterizedTest mais uma fonte de argumentos oferece.
  • Cada linha mostra expected ao lado de actual, então a linha 4 / expected=true / actual=false indica exatamente qual valor discordou — o mesmo valor diagnóstico que a mensagem de assertEquals do JUnit e o modelo name = "..." fornecem.

Quando usar um teste parametrizado

Use @ParameterizedTest quando um comportamento deve se manter em uma tabela de entradas — valores de fronteira, classes de equivalência ou uma lista de regressão de entradas que já causaram problemas. Continue usando @Test simples quando um cenário precisa de configuração única ou asserções distintas; forçar casos não relacionados em um único método parametrizado só torna o relatório mais difícil de ler. Para configuração compartilhada em ambos os estilos, veja o capítulo sobre ciclo de vida de testes, e para o vocabulário completo de asserções usado dentro de cada execução, o capítulo sobre asserções.

Prática

Prática
No JUnit 5, seu teste precisa executar uma vez para cada linha de uma tabela onde cada linha tem uma string de entrada e a string invertida esperada. Qual é a anotação única mais apropriada para fornecer esses dados inline?
No JUnit 5, seu teste precisa executar uma vez para cada linha de uma tabela onde cada linha tem uma string de entrada e a string invertida esperada. Qual é a anotação única mais apropriada para fornecer esses dados inline?
Was this page helpful?