A TestNG-like dataprovider runner for JUnit having a simplified syntax compared to all the existing JUnit features.
—
This document covers the utility classes and helper methods that simplify creating and manipulating data provider arrays.
import com.tngtech.java.junit.dataprovider.DataProviders;
import static com.tngtech.java.junit.dataprovider.DataProviders.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;The DataProviders class provides static helper methods for creating data provider arrays with less boilerplate code.
public class DataProviders {
/**
* Create Object array containing all given arguments.
* @param args arguments to include in array
* @return Object array containing all args
*/
public static Object[] $(Object... args)
/**
* Create array of Object arrays from given arrays.
* @param args Object arrays to combine
* @return array of Object arrays
*/
public static Object[][] $$(Object[]... args)
/**
* Create dataprovider test for each argument.
* @param args arguments to wrap individually
* @return array containing Object arrays for each single argument
*/
public static Object[][] testForEach(Object... args)
/**
* Create dataprovider test for each element in Iterable.
* @param args Iterable to convert
* @return array containing Object arrays for each element
* @deprecated since 1.12.0 - Iterable can be returned directly
*/
@Deprecated
public static <T> Object[][] testForEach(Iterable<T> args)
/**
* Create dataprovider test for each enum value.
* @param enumClass enum Class to get values from
* @return array containing Object arrays for each enum value
*/
public static <E extends Enum<E>> Object[][] testForEach(Class<E> enumClass)
/**
* Create cross product of two data providers.
* @param rows1 first dataprovider data
* @param rows2 second dataprovider data
* @return cross product combining all row combinations
*/
public static Object[][] crossProduct(Object[][] rows1, Object[][] rows2)
}The $() and $$() methods provide a concise syntax for creating data provider arrays:
import static com.tngtech.java.junit.dataprovider.DataProviders.*;
@DataProvider
public static Object[][] testData() {
return $$(
$("hello", 5, true),
$("world", 5, true),
$("", 0, false),
$("testing", 7, true)
);
}
// Equivalent to:
@DataProvider
public static Object[][] testDataVerbose() {
return new Object[][] {
new Object[] { "hello", 5, true },
new Object[] { "world", 5, true },
new Object[] { "", 0, false },
new Object[] { "testing", 7, true }
};
}Use testForEach() when each data item should be a separate test case:
@DataProvider
public static Object[][] stringValues() {
return testForEach("alpha", "beta", "gamma", "delta");
}
// Equivalent to:
// return new Object[][] { {"alpha"}, {"beta"}, {"gamma"}, {"delta"} };
@Test
@UseDataProvider("stringValues")
public void testSingleString(String value) {
assertNotNull(value);
assertTrue(value.length() > 0);
}Test all values of an enum easily:
enum Priority { LOW, MEDIUM, HIGH, CRITICAL }
@DataProvider
public static Object[][] allPriorities() {
return testForEach(Priority.class);
}
// Equivalent to:
// return new Object[][] { {LOW}, {MEDIUM}, {HIGH}, {CRITICAL} };
@Test
@UseDataProvider("allPriorities")
public void testPriorityHandling(Priority priority) {
assertNotNull(priority);
// Test priority-specific logic
}While still available, direct Iterable return is now preferred:
// Deprecated approach
@DataProvider
public static Object[][] fromList() {
List<String> items = Arrays.asList("item1", "item2", "item3");
return testForEach(items);
}
// Preferred approach (since 1.12.0)
@DataProvider
public static List<String> fromListDirect() {
return Arrays.asList("item1", "item2", "item3");
}Generate all combinations of two data sets:
@DataProvider
public static Object[][] userTypes() {
return $$($("admin"), $("user"), $("guest"));
}
@DataProvider
public static Object[][] permissions() {
return $$($("read"), $("write"), $("delete"));
}
@DataProvider
public static Object[][] userPermissionCombinations() {
return crossProduct(userTypes(), permissions());
}
// Results in:
// [["admin", "read"], ["admin", "write"], ["admin", "delete"],
// ["user", "read"], ["user", "write"], ["user", "delete"],
// ["guest", "read"], ["guest", "write"], ["guest", "delete"]]
@Test
@UseDataProvider("userPermissionCombinations")
public void testUserPermissions(String userType, String permission) {
// Test all user type and permission combinations
}Combine utilities with dynamic data generation:
@DataProvider
public static Object[][] dynamicRangeData() {
List<Object[]> data = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
data.add($("value" + i, i * 10, i % 2 == 0));
}
return data.toArray(new Object[0][]);
}
// Or using testForEach with computed values
@DataProvider
public static Object[][] computedValues() {
List<Integer> values = IntStream.range(1, 10)
.boxed()
.collect(Collectors.toList());
return testForEach(values);
}Generate complex combinations with multiple dimensions:
@DataProvider
public static Object[][] browsers() {
return testForEach("chrome", "firefox", "safari");
}
@DataProvider
public static Object[][] resolutions() {
return testForEach("1920x1080", "1366x768", "390x844");
}
@DataProvider
public static Object[][] platforms() {
return testForEach("windows", "mac", "linux");
}
@DataProvider
public static Object[][] allEnvironmentCombinations() {
Object[][] browserRes = crossProduct(browsers(), resolutions());
return crossProduct(browserRes, platforms());
}
@Test
@UseDataProvider("allEnvironmentCombinations")
public void testAcrossEnvironments(String browser, String resolution, String platform) {
// Test with all browser/resolution/platform combinations
}Create specialized data sets:
@DataProvider
public static Object[][] positiveNumbers() {
return testForEach(1, 5, 10, 100, 1000);
}
@DataProvider
public static Object[][] negativeNumbers() {
return testForEach(-1, -5, -10, -100, -1000);
}
@DataProvider
public static Object[][] allNumbers() {
return crossProduct(
$$($("positive")),
positiveNumbers()
).concat(crossProduct(
$$($("negative")),
negativeNumbers()
));
}
// Manual concatenation helper
private static Object[][] concat(Object[][] array1, Object[][] array2) {
Object[][] result = new Object[array1.length + array2.length][];
System.arraycopy(array1, 0, result, 0, array1.length);
System.arraycopy(array2, 0, result, array1.length, array2.length);
return result;
}Combine utilities with external data sources:
@DataProvider
public static Object[][] fromCSVFile() throws IOException {
List<Object[]> data = new ArrayList<>();
try (BufferedReader reader = Files.newBufferedReader(Paths.get("test-data.csv"))) {
String line;
while ((line = reader.readLine()) != null) {
String[] parts = line.split(",");
data.add($(parts[0], Integer.parseInt(parts[1]), Boolean.parseBoolean(parts[2])));
}
}
return data.toArray(new Object[0][]);
}@DataProvider
public static Object[][] fromDatabase() {
// Assuming you have a database connection
List<Object[]> data = new ArrayList<>();
String sql = "SELECT name, age, active FROM test_users";
try (PreparedStatement stmt = connection.prepareStatement(sql);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
data.add($(rs.getString("name"), rs.getInt("age"), rs.getBoolean("active")));
}
} catch (SQLException e) {
throw new RuntimeException("Failed to load test data", e);
}
return data.toArray(new Object[0][]);
}@DataProvider
public static Object[][] fromJSONFile() throws IOException {
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(new File("test-data.json"));
List<Object[]> data = new ArrayList<>();
for (JsonNode item : root) {
data.add($(
item.get("name").asText(),
item.get("value").asInt(),
item.get("enabled").asBoolean()
));
}
return data.toArray(new Object[0][]);
}For large data sets, consider lazy loading or streaming approaches:
@DataProvider
public static Iterator<Object[]> largeDataSet() {
return new Iterator<Object[]>() {
private int current = 0;
private final int max = 100000;
@Override
public boolean hasNext() {
return current < max;
}
@Override
public Object[] next() {
return $("item" + current, current++);
}
};
}Reuse common data structures:
private static final Object[][] COMMON_STRINGS = testForEach(
"alpha", "beta", "gamma", "delta"
);
@DataProvider
public static Object[][] reusedData() {
return COMMON_STRINGS; // Reuse instead of recreating
}Install with Tessl CLI
npx tessl i tessl/maven-com-tngtech-java--junit-dataprovider