Skip to content

Latest commit

 

History

History
856 lines (683 loc) · 34.4 KB

writing-tests.adoc

File metadata and controls

856 lines (683 loc) · 34.4 KB

Writing Tests

A first test case
link:{testDir}/example/FirstJUnit5Tests.java[role=include]

Annotations

JUnit Jupiter supports the following annotations for configuring tests and extending the framework.

All core annotations are located in the {api-package} package in the junit-jupiter-api module.

Annotation Description

@Test

Denotes that a method is a test method. Unlike JUnit 4’s @Test annotation, this annotation does not declare any attributes, since test extensions in JUnit Jupiter operate based on their own dedicated annotations.

@RepeatedTest

Denotes that a method is a test template for a repeated test

@TestFactory

Denotes that a method is a test factory for dynamic tests

@DisplayName

Declares a custom display name for the test class or test method

@BeforeEach

Denotes that the annotated method should be executed before each @Test method in the current class; analogous to JUnit 4’s @Before. Such methods are inherited.

@AfterEach

Denotes that the annotated method should be executed after each @Test method in the current class; analogous to JUnit 4’s @After. Such methods are inherited.

@BeforeAll

Denotes that the annotated method should be executed before all @Test methods in the current class; analogous to JUnit 4’s @BeforeClass. Such methods must be static and are inherited.

@AfterAll

Denotes that the annotated method should be executed after all @Test methods in the current class; analogous to JUnit 4’s @AfterClass. Such methods must be static and are inherited.

@Nested

Denotes that the annotated class is a nested, non-static test class. Due to restrictions of the Java language, @BeforeAll and @AfterAll methods cannot be used in a @Nested test class.

@Tag

Used to declare tags for filtering tests, either at the class or method level; analogous to test groups in TestNG or Categories in JUnit 4

@Disabled

Used to disable a test class or test method; analogous to JUnit 4’s @Ignore

@ExtendWith

Used to register custom extensions

Meta-Annotations and Composed Annotations

JUnit Jupiter annotations can be used as meta-annotations. That means that you can define your own composed annotation that will automatically inherit the semantics of its meta-annotations.

For example, instead of copying and pasting @Tag("fast") throughout your code base (see Tagging and Filtering), you can create a custom composed annotation named @Fast as follows. @Fast can then be used as a drop-in replacement for @Tag("fast").

link:{testDir}/example/Fast.java[role=include]

Standard Test Class

A standard test case
link:{testDir}/example/StandardTests.java[role=include]
Note
Neither test classes nor test methods need to be public.

Display Names

Test classes and test methods can declare custom display names — with spaces, special characters, and even emojis — that will be displayed by test runners and test reporting.

link:{testDir}/example/DisplayNameDemo.java[role=include]

Assertions

JUnit Jupiter comes with many of the assertion methods that JUnit 4 has and adds a few that lend themselves well to being used with Java 8 lambdas. All JUnit Jupiter assertions are static methods in the {Assertions} class.

link:{testDir}/example/AssertionsDemo.java[role=include]

Third-party Assertion Libraries

Even though the assertion facilities provided by JUnit Jupiter are sufficient for many testing scenarios, there are times when more power and additional functionality such as matchers are desired or required. In such cases, the JUnit team recommends the use of third-party assertion libraries such as {AssertJ}, {Hamcrest}, {Truth}, etc. Developers are therefore free to use the assertion library of their choice.

For example, the combination of matchers and a fluent API can be used to make assertions more descriptive and readable. However, JUnit Jupiter’s {Assertions} class does not provide an assertThat() method like the one found in JUnit 4’s org.junit.Assert class which accepts a Hamcrest Matcher. Instead, developers are encouraged to use the built-in support for matchers provided by third-party assertion libraries.

The following example demonstrates how to use the assertThat() support from Hamcrest in a JUnit Jupiter test. As long as the Hamcrest library has been added to the classpath, you can statically import methods such as assertThat(), is(), and equalTo() and then use them in tests like in the assertWithHamcrestMatcher() method below.

link:{testDir}/example/HamcrestAssertionDemo.java[role=include]

Naturally, legacy tests based on the JUnit 4 programming model can continue using org.junit.Assert#assertThat.

Assumptions

JUnit Jupiter comes with a subset of the assumption methods that JUnit 4 provides and adds a few that lend themselves well to being used with Java 8 lambdas. All JUnit Jupiter assumptions are static methods in the {Assumptions} class.

link:{testDir}/example/AssumptionsDemo.java[role=include]

Disabling Tests

Here’s a disabled test case.

link:{testDir}/example/DisabledClassDemo.java[role=include]

And here’s a test case with a disabled test method.

link:{testDir}/example/DisabledTestsDemo.java[role=include]

Tagging and Filtering

Test classes and methods can be tagged. Those tags can later be used to filter test discovery and execution.

link:{testDir}/example/TaggingDemo.java[role=include]

Nested Tests

Nested tests give the test writer more capabilities to express the relationship among several group of tests. Here’s an elaborate example.

Nested test suite for testing a stack
link:{testDir}/example/TestingAStackDemo.java[role=include]
Note
Only non-static nested classes (i.e. inner classes) can serve as @Nested tests. Nesting can be arbitrarily deep, and those inner classes are considered to be full members of the test class family with one exception: @BeforeAll and @AfterAll do not work, because Java does not allow static members in inner classes.

Dependency Injection for Constructors and Methods

In all prior JUnit versions, test constructors or methods were not allowed to have parameters (at least not with the standard Runner implementations). As one of the major changes in JUnit Jupiter, both test constructors and methods are now permitted to have parameters. This allows for greater flexibility and enables Dependency Injection for constructors and methods.

{ParameterResolver} defines the API for test extensions that wish to dynamically resolve parameters at runtime. If a test constructor or a @Test, @TestFactory, @BeforeEach, @AfterEach, @BeforeAll, or @AfterAll method accepts a parameter, the parameter must be resolved at runtime by a registered ParameterResolver.

There are currently three built-in resolvers that are registered automatically.

  • {TestInfoParameterResolver}: if a method parameter is of type {TestInfo}, the TestInfoParameterResolver will supply an instance of TestInfo corresponding to the current test as the value for the parameter. The TestInfo can then be used to retrieve information about the current test such as the test’s display name, the test class, the test method, or associated tags. The display name is either a technical name, such as the name of the test class or test method, or a custom name configured via @DisplayName.

    {TestInfo} acts as a drop-in replacement for the TestName rule from JUnit 4. Here is an example of its usage.

link:{testDir}/example/TestInfoDemo.java[role=include]
  • RepetitionInfoParameterResolver: if a method parameter in a @RepeatedTest, @BeforeEach, or @AfterEach method is of type {RepetitionInfo}, the RepetitionInfoParameterResolver will supply an instance of RepetitionInfo. RepetitionInfo can then be used to retrieve information about the current repetition and the total number of repetitions for the corresponding @RepeatedTest. Note, however, that RepetitionInfoParameterResolver is not registered outside the context of a @RepeatedTest. See Repeated Test Examples.

  • {TestReporterParameterResolver}: if a method parameter is of type {TestReporter}, the TestReporterParameterResolver will supply an instance of TestReporter. The TestReporter can be used to publish additional data about the current test run. The data can be consumed through {TestExecutionListener}.reportingEntryPublished() and thus be viewed by IDEs or included in reports.

    In JUnit Jupiter you should use TestReporter where you used to print information to stdout or stderr in JUnit 4. Using @RunWith(JUnitPlatform.class) will even output all reported entries to stdout.

link:{testDir}/example/TestReporterDemo.java[role=include]
Note
Other parameter resolvers must be explicitly enabled by registering appropriate extensions via @ExtendWith.

Check out the {MockitoExtension} for an example of a custom {ParameterResolver}. While not intended to be production-ready, it demonstrates the simplicity and expressiveness of both the extension model and the parameter resolution process. MyMockitoTest demonstrates how to inject Mockito mocks into @BeforeEach and @Test methods.

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import com.example.Person;
import com.example.mockito.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class MyMockitoTest {

	@BeforeEach
	void init(@Mock Person person) {
		when(person.getName()).thenReturn("Dilbert");
	}

	@Test
	void simpleTestWithInjectedMock(@Mock Person person) {
		assertEquals("Dilbert", person.getName());
	}

}

Test Interfaces and Default Methods

JUnit Jupiter allows @Test, @TestFactory, @BeforeEach, and @AfterEach to be declared on interface default methods. In addition, @BeforeAll and @AfterAll can be declared on static methods in a test interface, while @ExtendWith and @Tag can be declared on test interfaces to configure extensions and tags. Here are some examples.

link:{testDir}/example/testinterface/TestLifecycleLogger.java[role=include]
link:{testDir}/example/testinterface/TestInterfaceDynamicTestsDemo.java[role=include]

@ExtendWith and @Tag can be declared on a test interface so that classes that implement it automatically inherit its tags and extensions. See [extensions-lifecycle-callbacks-before-after-execution] for the source code of the TimingExtension.

link:{testDir}/example/testinterface/TimeExecutionLogger.java[role=include]

In your test class you can then implement these test interfaces to have them applied.

link:{testDir}/example/testinterface/TestInterfaceDemo.java[role=include]

Running the TestInterfaceDemo results in output similar to the following:

:junitPlatformTest
18:28:13.967 [main] INFO  example.testinterface.TestLifecycleLogger - beforeAllTests
18:28:13.982 [main] INFO  example.testinterface.TestLifecycleLogger - About to execute [dynamicTestsFromCollection()]
18:28:14.000 [main] INFO  example.testinterface.TimingExtension - Method [dynamicTestsFromCollection] took 13 ms.
18:28:14.004 [main] INFO  example.testinterface.TestLifecycleLogger - Finished executing [dynamicTestsFromCollection()]
18:28:14.007 [main] INFO  example.testinterface.TestLifecycleLogger - About to execute [isEqualValue()]
18:28:14.008 [main] INFO  example.testinterface.TimingExtension - Method [isEqualValue] took 1 ms.
18:28:14.009 [main] INFO  example.testinterface.TestLifecycleLogger - Finished executing [isEqualValue()]
18:28:14.011 [main] INFO  example.testinterface.TestLifecycleLogger - afterAllTests

Test run finished after 190 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         3 tests found           ]
[         0 tests skipped         ]
[         3 tests started         ]
[         0 tests aborted         ]
[         3 tests successful      ]
[         0 tests failed          ]

BUILD SUCCESSFUL

Another possible application of this feature is to write tests for interface contracts. For example, you can write tests for how implementations of Object.equals or Comparable.compareTo should behave as follows.

link:{testDir}/example/defaultmethods/Testable.java[role=include]
link:{testDir}/example/defaultmethods/EqualsContract.java[role=include]
link:{testDir}/example/defaultmethods/ComparableContract.java[role=include]

In your test class you can then implement both contract interfaces thereby inheriting the corresponding tests. Of course you’ll have to implement the abstract methods.

link:{testDir}/example/defaultmethods/StringTests.java[role=include]

The above tests are merely meant as examples and therefore not complete.

Repeated Tests

JUnit Jupiter provides the ability to repeat a test a specified number of times simply by annotating a method with @RepeatedTest and specifying the total number of repetitions desired. Each invocation of a repeated test behaves like the execution of a regular @Test method with full support for the same lifecycle callbacks and extensions.

The following example demonstrates how to declare a test named repeatedTest() that will be automatically repeated 10 times.

@RepeatedTest(10)
void repeatedTest() {
	// ...
}

In addition to specifying the number of repetitions, a custom display name can be configured for each repetition via the name attribute of the @RepeatedTest annotation. Furthermore, the display name can be a pattern composed of a combination of static text and dynamic placeholders. The following placeholders are currently supported.

  • {displayName}: display name of the @RepeatedTest method

  • {currentRepetition}: the current repetition count

  • {totalRepetitions}: the total number of repetitions

The default display name for a given repetition is generated based on the following pattern: "repetition {currentRepetition} of {totalRepetitions}". Thus, the display names for individual repetitions of the previous repeatedTest() example would be: repetition 1 of 10, repetition 2 of 10, etc. If you would like the display name of the @RepeatedTest method included in the name of each repetition, you can define your own custom pattern or use the predefined RepeatedTest.LONG_DISPLAY_NAME pattern. The latter is equal to "{displayName} :: repetition {currentRepetition} of {totalRepetitions}" which results in display names for individual repetitions like repeatedTest() :: repetition 1 of 10, repeatedTest() :: repetition 2 of 10, etc.

In order to retrieve information about the current repetition and the total number of repetitions programmatically, a developer can choose to have an instance of RepetitionInfo injected into a @RepeatedTest, @BeforeEach, or @AfterEach method.

Repeated Test Examples

The RepeatedTestsDemo class at the end of this section demonstrates several examples of repeated tests.

The repeatedTest() method is identical to example from the previous section; whereas, repeatedTestWithRepetitionInfo() demonstrates how to have an instance of RepetitionInfo injected into a test to access the total number of repetitions for the current repeated test.

The next two methods demonstrate how to include a custom @DisplayName for the @RepeatedTest method in the display name of each repetition. customDisplayName() combines a custom display name with a custom pattern and then uses TestInfo to verify the format of the generated display name. Repeat! is the {displayName} which comes from the @DisplayName declaration, and 1/1 comes from {currentRepetition}/{totalRepetitions}. In contrast, customDisplayNameWithLongPattern() uses the aforementioned predefined RepeatedTest.LONG_DISPLAY_NAME pattern.

repeatedTestInGerman() demonstrates the ability to translate display names of repeated tests into foreign languages — in this case German, resulting in names for individual repetitions such as: Wiederholung 1 von 5, Wiederholung 2 von 5, etc.

Since the beforeEach() method is annotated with @BeforeEach it will get executed before each repetition of each repeated test. By having the TestInfo and RepetitionInfo injected into the method, we see that it’s possible to obtain information about the currently executing repeated test. Executing RepeatedTestsDemo with the INFO log level enabled results in the following output.

INFO: About to execute repetition 1 of 10 for repeatedTest
INFO: About to execute repetition 2 of 10 for repeatedTest
INFO: About to execute repetition 3 of 10 for repeatedTest
INFO: About to execute repetition 4 of 10 for repeatedTest
INFO: About to execute repetition 5 of 10 for repeatedTest
INFO: About to execute repetition 6 of 10 for repeatedTest
INFO: About to execute repetition 7 of 10 for repeatedTest
INFO: About to execute repetition 8 of 10 for repeatedTest
INFO: About to execute repetition 9 of 10 for repeatedTest
INFO: About to execute repetition 10 of 10 for repeatedTest
INFO: About to execute repetition 1 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 2 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 3 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 4 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 5 of 5 for repeatedTestWithRepetitionInfo
INFO: About to execute repetition 1 of 1 for customDisplayName
INFO: About to execute repetition 1 of 1 for customDisplayNameWithLongPattern
INFO: About to execute repetition 1 of 5 for repeatedTestInGerman
INFO: About to execute repetition 2 of 5 for repeatedTestInGerman
INFO: About to execute repetition 3 of 5 for repeatedTestInGerman
INFO: About to execute repetition 4 of 5 for repeatedTestInGerman
INFO: About to execute repetition 5 of 5 for repeatedTestInGerman
link:{testDir}/example/RepeatedTestsDemo.java[role=include]

When using the ConsoleLauncher or the junitPlatformTest Gradle plugin with the unicode theme enabled, execution of RepeatedTestsDemo results in the following output to the console.

├─ RepeatedTestsDemo ✔
│  ├─ repeatedTest() ✔
│  │  ├─ repetition 1 of 10 ✔
│  │  ├─ repetition 2 of 10 ✔
│  │  ├─ repetition 3 of 10 ✔
│  │  ├─ repetition 4 of 10 ✔
│  │  ├─ repetition 5 of 10 ✔
│  │  ├─ repetition 6 of 10 ✔
│  │  ├─ repetition 7 of 10 ✔
│  │  ├─ repetition 8 of 10 ✔
│  │  ├─ repetition 9 of 10 ✔
│  │  └─ repetition 10 of 10 ✔
│  ├─ repeatedTestWithRepetitionInfo(RepetitionInfo) ✔
│  │  ├─ repetition 1 of 5 ✔
│  │  ├─ repetition 2 of 5 ✔
│  │  ├─ repetition 3 of 5 ✔
│  │  ├─ repetition 4 of 5 ✔
│  │  └─ repetition 5 of 5 ✔
│  ├─ Repeat! ✔
│  │  └─ Repeat! 1/1 ✔
│  ├─ Details... ✔
│  │  └─ Details... :: repetition 1 of 1 ✔
│  └─ repeatedTestInGerman() ✔
│     ├─ Wiederholung 1 von 5 ✔
│     ├─ Wiederholung 2 von 5 ✔
│     ├─ Wiederholung 3 von 5 ✔
│     ├─ Wiederholung 4 von 5 ✔
│     └─ Wiederholung 5 von 5 ✔

Parameterized Tests

Parameterized tests make it possible to run a test multiple times with different arguments. They are declared just like regular @Test methods but use the @ParameterizedTest annotation instead. In addition, you must declare at least one source that will provide the arguments for each invocation.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]

This parameterized test uses the @ValueSource annotation to specify a String array as the source of arguments. When executing this method, each invocation will be reported separately. For instance, the ConsoleLauncher will print output similar to the following.

testWithStringParameter(String) ✔
├─ [1] Hello ✔
└─ [2] World ✔

Required Setup

In order to use parameterized tests you need to add a dependency on the junit-jupiter-params artifact. Please refer to [dependency-metadata] for details.

Sources of Arguments

Out of the box, JUnit Jupiter provides quite a few source annotations. Each of the following subsections provides a brief overview and an example for each of them. Please refer to the JavaDoc in the {params-provider-package} package for additional information.

@ValueSource

@ValueSource is one of the simplest possible sources. It lets you specify an array of literals of primitive types (either String, int, long, or double) and can only be used for providing a single parameter per invocation.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]
@EnumSource

@EnumSource provides a convenient way to use Enum constants. The annotation provides an optional names parameter that lets you specify which constants shall be used. If omitted, all constants will be used like in the following example.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]
@MethodSource

@MethodSource allows to refer to one or multiple methods of the test class. Each method must return a Stream, an Iterable, an Iterator, or an array of arguments. In addition, each method must be static and must not accept any arguments.

If you only need a single parameter, you can return instances of the parameter type directly as demonstrated by the following example.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]

In case you need multiple parameters, you need to return Argument instances as shown below. Note that arguments(Object…​) is a static factory method defined in org.junit.jupiter.params.provider.ObjectArrayArguments.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]
@CsvSource

@CsvSource allows you to express argument lists as comma-separated values (i.e., String literals).

link:{testDir}/example/ParameterizedTestDemo.java[role=include]
@CsvFileSource

@CsvFileSource lets you use CSV files from the classpath. Each line from a CSV file results in one invocation of the parameterized test.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]
two-column.csv
link:{testResourcesDir}/two-column.csv[role=include]
@ArgumentsSource

@ArgumentsSource can be used to specify a custom, reusable ArgumentsProvider.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]

Argument Conversion

Implicit Conversion

To support use cases like @CsvSource, JUnit Jupiter provides a number of built-in implicit type converters. The conversion process depends on the declared type of each method parameter.

For example, if a @ParameterizedTest declares a parameter of type TimeUnit and the actual type supplied by the declared source is a String, the string will automatically be converted into the corresponding TimeUnit enum constant.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]

String instances are currently implicitly converted to the following target types.

Target Type Example

boolean/Boolean

"true"true

byte/Byte

"1"(byte) 1

char/Character

"o"'o'

short/Short

"1"(short) 1

int/Integer

"1"1

long/Long

"1"1L

float/Float

"1.0"1.0f

double/Double

"1.0"1.0d

Enum subclass

"SECONDS"TimeUnit.SECONDS

java.time.Instant

"1970-01-01T00:00:00Z"Instant.ofEpochMilli(0)

java.time.LocalDate

"2017-03-14"LocalDate.of(2017, 3, 14)

java.time.LocalDateTime

"2017-03-14T12:34:56.789"LocalDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000)

java.time.LocalTime

"12:34:56.789"LocalTime.of(12, 34, 56, 789_000_000)

java.time.OffsetDateTime

"2017-03-14T12:34:56.789Z"OffsetDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)

java.time.OffsetTime

"12:34:56.789Z"OffsetTime.of(12, 34, 56, 789_000_000, ZoneOffset.UTC)

java.time.Year

"2017"Year.of(2017)

java.time.YearMonth

"2017-03"YearMonth.of(2017, 3)

java.time.ZonedDateTime

"2017-03-14T12:34:56.789Z"ZonedDateTime.of(2017, 3, 14, 12, 34, 56, 789_000_000, ZoneOffset.UTC)

Explicit Conversion

Instead of using implicit argument conversion you may explicitly specify an ArgumentConverter to use for a certain parameter using the @ConvertWith annotation like in the following example.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]

Explicit argument converters are meant to be implemented by test authors. Thus, junit-jupiter-params only provides a single explicit argument converter that may also serve as a reference implementation: JavaTimeArgumentConverter. It is used via the composed annotation JavaTimeConversionPattern.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]

Customizing Display Names

By default, the display name of a parameterized test invocation contains the invocation index and the String representation of all arguments for that specific invocation. However, you can customize invocation display names via the name attribute of the @ParameterizedTest annotation like in the following example.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]

When executing the above method using the ConsoleLauncher you will see output similar to the following.

Display name of container ✔
├─ 1 ==> first='foo', second=1 ✔
├─ 2 ==> first='bar', second=2 ✔
└─ 3 ==> first='baz, qux', second=3 ✔

The following placeholders are supported within custom display names.

Placeholder Description

{index}

the current invocation index (1-based)

{arguments}

the complete, comma-separated arguments list

{0}, {1}, …​

an individual argument

Lifecycle and Interoperability

Each invocation of a parameterized test has the same lifecycle as a regular @Test method. For example, @BeforeEach methods will be executed before each invocation. Similar to Dynamic Tests, invocations will appear one by one in the test tree of an IDE. You may at will mix regular @Test methods and @ParameterizedTest methods within the same test class.

You may use ParameterResolver extensions with @ParameterizedTest methods. However, method parameters that are resolved by argument sources need to come first in the argument list.

link:{testDir}/example/ParameterizedTestDemo.java[role=include]

Dynamic Tests

The standard @Test annotation in JUnit Jupiter described in Annotations is very similar to the @Test annotation in JUnit 4. Both describe methods that implement test cases. These test cases are static in the sense that they are fully specified at compile time, and their behavior cannot be changed by anything happening at runtime. Assumptions provide a basic form of dynamic behavior but are intentionally rather limited in their expressiveness.

In addition to these standard tests a completely new kind of test programming model has been introduced in JUnit Jupiter. This new kind of test is a dynamic test which is generated at runtime by a factory method that is annotated with @TestFactory.

In contrast to @Test methods, a @TestFactory method is not itself a test case but rather a factory for test cases. Thus, a dynamic test is the product of a factory. Technically speaking, a @TestFactory method must return a Stream, Collection, Iterable, or Iterator of DynamicTest instances. These DynamicTest instances will then be executed lazily, enabling dynamic and even non-deterministic generation of test cases.

Any Stream returned by a @TestFactory will be properly closed by calling stream.close(), making it safe to use a resource such as Files.lines().

As with @Test methods, @TestFactory methods must not be private or static and may optionally declare parameters to be resolved by ParameterResolvers.

A DynamicTest is a test case generated at runtime. It is composed of a display name and an Executable. Executable is a @FunctionalInterface which means that the implementations of dynamic tests can be provided as lambda expressions or method references.

Warning
Dynamic Test Lifecycle
The execution lifecycle of a dynamic test is quite different than it is for a standard @Test case. Specifically, there are not any lifecycle callbacks for dynamic tests. This means that @BeforeEach and @AfterEach methods and their corresponding extension callbacks are not executed for dynamic tests. In other words, if you access fields from the test instance within a lambda expression for a dynamic test, those fields will not be reset by callback methods or extensions between the execution of dynamic tests generated by the same @TestFactory method.

As of JUnit Jupiter {jupiter-version}, dynamic tests must always be created by factory methods; however, this might be complemented by a registration facility in a later release.

Dynamic Test Examples

The following DynamicTestsDemo class demonstrates several examples of test factories and dynamic tests.

The first method returns an invalid return type. Since an invalid return type cannot be detected at compile time, a JUnitException is thrown when it is detected at runtime.

The next five methods are very simple examples that demonstrate the generation of a Collection, Iterable, Iterator, or Stream of DynamicTest instances. Most of these examples do not really exhibit dynamic behavior but merely demonstrate the supported return types in principle. However, dynamicTestsFromStream() and dynamicTestsFromIntStream() demonstrate how easy it is to generate dynamic tests for a given set of strings or a range of input numbers.

The last method is truly dynamic in nature. generateRandomNumberOfTests() implements an Iterator that generates random numbers, a display name generator, and a test executor and then provides all three to DynamicTest.stream(). Although the non-deterministic behavior of generateRandomNumberOfTests() is of course in conflict with test repeatability and should thus be used with care, it serves to demonstrate the expressiveness and power of dynamic tests.

link:{testDir}/example/DynamicTestsDemo.java[role=include]