link:{testDir}/example/FirstJUnit5Tests.java[role=include]
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 |
---|---|
|
Denotes that a method is a test method. Unlike JUnit 4’s |
|
Denotes that a method is a test template for a repeated test |
|
Denotes that a method is a test factory for dynamic tests |
|
Declares a custom display name for the test class or test method |
|
Denotes that the annotated method should be executed before each |
|
Denotes that the annotated method should be executed after each |
|
Denotes that the annotated method should be executed before all |
|
Denotes that the annotated method should be executed after all |
|
Denotes that the annotated class is a nested, non-static test class. Due to restrictions of the Java language, |
|
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 |
|
Used to disable a test class or test method; analogous to JUnit 4’s |
|
Used to register custom extensions |
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]
link:{testDir}/example/StandardTests.java[role=include]
Note
|
Neither test classes nor test methods need to be public .
|
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]
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]
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
.
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]
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]
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 give the test writer more capabilities to express the relationship among several group of tests. Here’s an elaborate example.
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.
|
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}
, theTestInfoParameterResolver
will supply an instance ofTestInfo
corresponding to the current test as the value for the parameter. TheTestInfo
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 theTestName
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}
, theRepetitionInfoParameterResolver
will supply an instance ofRepetitionInfo
.RepetitionInfo
can then be used to retrieve information about the current repetition and the total number of repetitions for the corresponding@RepeatedTest
. Note, however, thatRepetitionInfoParameterResolver
is not registered outside the context of a@RepeatedTest
. See Repeated Test Examples. -
{TestReporterParameterResolver}
: if a method parameter is of type{TestReporter}
, theTestReporterParameterResolver
will supply an instance ofTestReporter
. TheTestReporter
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 tostdout
orstderr
in JUnit 4. Using@RunWith(JUnitPlatform.class)
will even output all reported entries tostdout
.
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());
}
}
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.
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.
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 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 ✔
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.
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
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
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
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
allows you to express argument lists as comma-separated values (i.e.,
String
literals).
link:{testDir}/example/ParameterizedTestDemo.java[role=include]
@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]
link:{testResourcesDir}/two-column.csv[role=include]
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 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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]
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 |
---|---|
|
the current invocation index (1-based) |
|
the complete, comma-separated arguments list |
|
an individual argument |
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]
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.
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]