Dynamic tests in JUnit 5

Igor Stojanovski
Mar 23, 2018 · 3 min read

Although not a novelty concept, dynamic tests in the current form were introduced in JUnit 5. The main point of dynamic tests is to demonstrate that users can can generate tests dynamically, at runtime. This opens up possibilities for implementing some interesting data-driven test scenarios.

Unlike a standard @Test method, a dynamic test method should be annotated with @TestFactory and itself is not a test but a test generator. As such it needs to return Stream, Collection, Iterable, or Iterator of a DynamicNode implementation.

The JUnit 5 documentation still marks the dynamic tests as an experimental feature.

Dynamic tests example

The example generates tests based on a csv file:

test_name,firstaddend,secondaddend,total
first,1,2,3
second,5,6,11
third,10,12,22

It is a simple addition test. As the title line of the csv shows, the first value is going to be used as the name of the test and then follow the first and the second addend and the sum value.

@TestFactory
public Stream<DynamicTest> testFactory() {
InputStream inputFS = getClass().getClassLoader().getResourceAsStream("testData.csv");
BufferedReader br = new BufferedReader(new InputStreamReader(inputFS));

return br.lines().skip(1).map(mapCsvLineToDynamicTest());
}

private Function<String, DynamicTest> mapCsvLineToDynamicTest() {
return s -> {
final String[] values = s.split(",");
return dynamicTest(values[0], () -> assertAddition(values));
};
}

private void assertAddition(String[] values) {
assertThat(Integer.parseInt(values[1]) + Integer.parseInt(values[2]))
.isEqualTo(Integer.parseInt(values[3]));
}

After running the test factory method the IntelliJ IDEA generates the following output:

JUunit 5 Dynamic tests
JUunit 5 Dynamic tests

Parametrized tests in JUnit 4 comparison

Parametrized tests in JUnit 4 are very similar to the dynamic tests. We can achieve the same effect from the previous example with the following code:

@RunWith(Parameterized.class)
public class ParametrizedTestExample {

@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ "first", 1, 2, 3 }, { "second", 5, 6, 11 }, { "third", 10, 12, 22 }
});
}

private String description;
private int first;
private int second;
private int total;

public ParametrizedTestExample(String description, int first, int second, int total) {
this.description = description;
this.first = first;
this.second = second;
this.total = total;
}

@Test
public void shouldHaveCorrectTotal() {
assertThat(first + second).isEqualTo(total);
}
}

But this time the IntelliJ output looks differently, and it is obvious why.

JUnit 4 parametrized tests
JUnit 4 parametrized tests

With parametrized tests it is as if we have executed three separate tests. If we had a @Before method JUnit 4 would have executed it for each parameter set. Opposite to this when we use a @TestFactory it looks as though we have a single test. This is confirmed by the fact that if we have a @BeforeEach or @AfterEach methods they will be executed once per a @TestFactory method.

Another difference is that when you use the Parametrized runner, each test in that class will be run once for each parameter set. If you have 5 parameters sets and 3 tests that will be a total of 15 test executions. Contrary to that, you can have as many @TestFactory methods in the same class as you wish. JUnit 5 will execute each one exactly once.


Originally published at igorski.co.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade