JUnit Jupiter extension for parameterized tests
—
Advanced argument providers for enum constants, method-generated arguments, and field-based data sources.
Provides enum constants as test arguments with flexible selection modes and filtering options.
/**
* Provides enum constants as arguments to parameterized tests
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.0")
@ArgumentsSource(EnumArgumentsProvider.class)
@Repeatable(EnumSources.class)
@interface EnumSource {
/**
* Enum class to use (defaults to first parameter type if enum)
*/
Class<? extends Enum<?>> value() default Enum.class;
/**
* Specific enum constant names or regex patterns
*/
String[] names() default {};
/**
* Selection mode for filtering enum constants
*/
Mode mode() default INCLUDE;
/**
* Range start for enum constants (experimental)
*/
@API(status = EXPERIMENTAL, since = "5.12")
String from() default "";
/**
* Range end for enum constants (experimental)
*/
@API(status = EXPERIMENTAL, since = "5.12")
String to() default "";
/**
* Selection mode enum
*/
enum Mode {
/**
* Include only specified names (default)
*/
INCLUDE,
/**
* Exclude specified names
*/
EXCLUDE,
/**
* Include names matching all patterns
*/
MATCH_ALL,
/**
* Include names matching any pattern
*/
MATCH_ANY,
/**
* Exclude names matching patterns
*/
MATCH_NONE
}
}Usage Examples:
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
enum Planet {
MERCURY, VENUS, EARTH, MARS, JUPITER, SATURN, URANUS, NEPTUNE
}
enum Status {
ACTIVE, INACTIVE, PENDING, SUSPENDED
}
class EnumSourceExamples {
// All enum constants
@ParameterizedTest
@EnumSource(Planet.class)
void testAllPlanets(Planet planet) {
assertNotNull(planet);
assertNotNull(planet.name());
}
// Inferred enum type from parameter
@ParameterizedTest
@EnumSource
void testInferredEnum(Status status) {
assertNotNull(status);
}
// Specific enum constants
@ParameterizedTest
@EnumSource(value = Planet.class, names = {"EARTH", "MARS", "JUPITER"})
void testSpecificPlanets(Planet planet) {
assertTrue(planet == Planet.EARTH ||
planet == Planet.MARS ||
planet == Planet.JUPITER);
}
// Exclude specific constants
@ParameterizedTest
@EnumSource(value = Status.class, names = {"SUSPENDED"}, mode = EnumSource.Mode.EXCLUDE)
void testActiveStatuses(Status status) {
assertNotEquals(Status.SUSPENDED, status);
}
// Pattern matching - include
@ParameterizedTest
@EnumSource(value = Planet.class, names = {".*US$"}, mode = EnumSource.Mode.MATCH_ANY)
void testPlanetsEndingWithUs(Planet planet) {
// VENUS, URANUS
assertTrue(planet.name().endsWith("US"));
}
// Pattern matching - exclude
@ParameterizedTest
@EnumSource(value = Planet.class, names = {"^M.*"}, mode = EnumSource.Mode.MATCH_NONE)
void testPlanetsNotStartingWithM(Planet planet) {
// All except MERCURY, MARS
assertFalse(planet.name().startsWith("M"));
}
// Multiple patterns - match all
@ParameterizedTest
@EnumSource(value = Planet.class, names = {".*R.*", ".*N.*"}, mode = EnumSource.Mode.MATCH_ALL)
void testPlanetsWithRAndN(Planet planet) {
// SATURN, URANUS
assertTrue(planet.name().contains("R") && planet.name().contains("N"));
}
// Range selection (experimental)
@ParameterizedTest
@EnumSource(value = Planet.class, from = "EARTH", to = "JUPITER")
void testInnerPlanets(Planet planet) {
// EARTH, MARS, JUPITER
int ordinal = planet.ordinal();
assertTrue(ordinal >= Planet.EARTH.ordinal() &&
ordinal <= Planet.JUPITER.ordinal());
}
}Provides arguments from factory method return values with support for various return types.
/**
* Provides arguments from factory method return values
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.0")
@ArgumentsSource(MethodArgumentsProvider.class)
@Repeatable(MethodSources.class)
@interface MethodSource {
/**
* Factory method names (defaults to test method name if empty)
*/
String[] value() default "";
}Usage Examples:
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.Arguments;
import java.util.stream.Stream;
import java.util.*;
class MethodSourceExamples {
// Method name matches test method name
@ParameterizedTest
@MethodSource
void testWithNumbers(int number) {
assertTrue(number > 0);
}
static Stream<Integer> testWithNumbers() {
return Stream.of(1, 2, 3, 5, 8);
}
// Explicit method name
@ParameterizedTest
@MethodSource("stringProvider")
void testWithStrings(String value) {
assertNotNull(value);
assertFalse(value.isEmpty());
}
static Stream<String> stringProvider() {
return Stream.of("apple", "banana", "cherry");
}
// Multiple arguments using Arguments.of()
@ParameterizedTest
@MethodSource("userProvider")
void testWithMultipleArgs(String name, int age, boolean active) {
assertNotNull(name);
assertTrue(age >= 0);
}
static Stream<Arguments> userProvider() {
return Stream.of(
Arguments.of("Alice", 25, true),
Arguments.of("Bob", 30, false),
Arguments.of("Charlie", 35, true)
);
}
// Collection return type
@ParameterizedTest
@MethodSource("listProvider")
void testWithList(String fruit) {
assertNotNull(fruit);
}
static List<String> listProvider() {
return Arrays.asList("apple", "banana", "cherry");
}
// Array return type
@ParameterizedTest
@MethodSource("arrayProvider")
void testWithArray(int value) {
assertTrue(value > 0);
}
static int[] arrayProvider() {
return new int[]{1, 2, 3, 4, 5};
}
// Object array for multiple parameters
@ParameterizedTest
@MethodSource("objectArrayProvider")
void testWithObjectArray(String name, double price) {
assertNotNull(name);
assertTrue(price > 0);
}
static Object[][] objectArrayProvider() {
return new Object[][]{
{"Apple", 1.50},
{"Banana", 0.75},
{"Cherry", 2.00}
};
}
// Iterator return type
@ParameterizedTest
@MethodSource("iteratorProvider")
void testWithIterator(String value) {
assertNotNull(value);
}
static Iterator<String> iteratorProvider() {
return Arrays.asList("one", "two", "three").iterator();
}
// Multiple method sources
@ParameterizedTest
@MethodSource({"positiveNumbers", "negativeNumbers"})
void testWithMultipleSources(int number) {
assertNotEquals(0, number);
}
static Stream<Integer> positiveNumbers() {
return Stream.of(1, 2, 3);
}
static Stream<Integer> negativeNumbers() {
return Stream.of(-1, -2, -3);
}
// External class method source
@ParameterizedTest
@MethodSource("com.example.TestDataProvider#providePairs")
void testWithExternalSource(String key, String value) {
assertNotNull(key);
assertNotNull(value);
}
// Complex data structures
@ParameterizedTest
@MethodSource("complexDataProvider")
void testWithComplexData(Map<String, Object> data) {
assertNotNull(data);
assertFalse(data.isEmpty());
}
static Stream<Map<String, Object>> complexDataProvider() {
return Stream.of(
Map.of("name", "Product A", "price", 19.99, "available", true),
Map.of("name", "Product B", "price", 29.50, "available", false)
);
}
}Provides arguments from field values (experimental feature since JUnit 5.11).
/**
* Provides arguments from field values (experimental)
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = EXPERIMENTAL, since = "5.11")
@ArgumentsSource(FieldArgumentsProvider.class)
@Repeatable(FieldSources.class)
@interface FieldSource {
/**
* Field names (defaults to test method name if empty)
*/
String[] value() default "";
}Usage Examples:
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.FieldSource;
import org.junit.jupiter.params.provider.Arguments;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.*;
class FieldSourceExamples {
// Static field with same name as test method
static List<String> testWithFieldData = Arrays.asList("apple", "banana", "cherry");
@ParameterizedTest
@FieldSource
void testWithFieldData(String fruit) {
assertNotNull(fruit);
}
// Explicit field name
static int[] numbers = {1, 2, 3, 5, 8};
@ParameterizedTest
@FieldSource("numbers")
void testWithNumbers(int number) {
assertTrue(number > 0);
}
// Field containing Arguments
static List<Arguments> userTestData = Arrays.asList(
Arguments.of("Alice", 25, true),
Arguments.of("Bob", 30, false),
Arguments.of("Charlie", 35, true)
);
@ParameterizedTest
@FieldSource("userTestData")
void testWithUserData(String name, int age, boolean active) {
assertNotNull(name);
assertTrue(age >= 0);
}
// Supplier field for lazy evaluation
static Supplier<Stream<String>> lazyStringProvider = () ->
Stream.of("lazy1", "lazy2", "lazy3");
@ParameterizedTest
@FieldSource("lazyStringProvider")
void testWithLazyField(String value) {
assertNotNull(value);
assertTrue(value.startsWith("lazy"));
}
// Multiple fields
static List<String> fruits = Arrays.asList("apple", "banana");
static List<String> vegetables = Arrays.asList("carrot", "broccoli");
@ParameterizedTest
@FieldSource({"fruits", "vegetables"})
void testWithMultipleFields(String item) {
assertNotNull(item);
}
// External class field
@ParameterizedTest
@FieldSource("com.example.TestData#testValues")
void testWithExternalField(String value) {
assertNotNull(value);
}
}Container annotations for multiple enum and method source annotations.
/**
* Container annotation for multiple @EnumSource annotations
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.0")
@interface EnumSources {
EnumSource[] value();
}
/**
* Container annotation for multiple @MethodSource annotations
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.0")
@interface MethodSources {
MethodSource[] value();
}
/**
* Container annotation for multiple @FieldSource annotations
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = EXPERIMENTAL, since = "5.11")
@interface FieldSources {
FieldSource[] value();
}Complex Method Source Patterns:
class AdvancedMethodSourceExamples {
// Parameterized test with test case names
@ParameterizedTest(name = "Test case: {0}")
@MethodSource("namedTestCases")
void testWithNamedCases(String testCase, int input, int expected) {
// Test implementation using testCase description
assertNotNull(testCase);
assertTrue(input >= 0);
assertTrue(expected >= 0);
}
static Stream<Arguments> namedTestCases() {
return Stream.of(
Arguments.of("Square root of 4", 4, 2),
Arguments.of("Square root of 9", 9, 3),
Arguments.of("Square root of 16", 16, 4)
);
}
// Dynamic data generation
@ParameterizedTest
@MethodSource("dynamicDataProvider")
void testWithDynamicData(int value) {
assertTrue(value > 0);
}
static Stream<Integer> dynamicDataProvider() {
return Stream.iterate(1, n -> n < 100, n -> n * 2);
}
// Combining different data sources
@ParameterizedTest
@MethodSource("combinedDataProvider")
void testWithCombinedData(String type, Object value) {
assertNotNull(type);
assertNotNull(value);
}
static Stream<Arguments> combinedDataProvider() {
return Stream.concat(
Stream.of("string").map(s -> Arguments.of("String", s)),
Stream.of(42).map(i -> Arguments.of("Integer", i))
);
}
}These advanced source types provide powerful capabilities for data-driven testing, enabling complex test scenarios with enum-based logic, dynamic data generation, and external data sources.
Install with Tessl CLI
npx tessl i tessl/maven-org-junit-jupiter--junit-jupiter-params