JUnit 5 Quick Start Guide and Advanced
Table of contents
- JUnit 5 Quick Start Guide and Advanced
- General changes
- New features: Basics
- New features: Advanced
- Advanced Test-Samples
- Milestone 3 Changes
- Closing words
Testing is important. We should all know that. They can help reproduce and fix problems in our code. They can also remind you that you shouldn’t have done something long after you forgot that you shouldn’t do that thing and a lot more.
The problem is often that developers see testing as some overhead for developing that should be done after the code is
finished. That’s wrong: Testing should be a vital part of development!
That problem is something JUnit 5 can help you with. It introduced a lot of interesting, fast ways to create tests. Be it Test-Factories, Test-Extensions (that only have to be written once), Lambda-Support etc. It pushed the D.R.Y. principle a lot!
To understand this guide you should know the basics of testing and JUnit (4), but otherwise you wouldn’t be here I guess.
You can also check out the best-practice-part included in this repository. It was created by request of a colleague:
Checkout JUnit best-practices included in this repository
Also JUnit 5 supports running parallel to JUnit 4 if that’s something you need.
Some headers related to code will have a code-link behind their name directing to the corresponding class in the
For the whole source code see my GitHub-Repository:
For an IDE I recommend IntelliJ IDEA 2016.2(+) right now since it brings native support for JUnit 5.
As for dependencies:
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.0.0-M3</version> <scope>compile</scope> </dependency>
<dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-runner</artifactId> <version>1.0.0-M3</version> <scope>compile</scope> </dependency>
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.0.0-M3</version> <scope>runtime</scope> </dependency>
testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.0.0-M3'
testCompile group: 'org.junit.platform', name: 'junit-platform-runner', version: '1.0.0-M3'
testRuntime group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.0.0-M3'
General changes (code)
This paragraph contains the small or general changes made in the transition from JUnit 4 to JUnit 5. Those are simple but still note worthy.
The first change is made to the most basic of things: the test and the
@Test-annotation themselves. You no longer
need to make the test
public, however you can still not make it
private. Also timeout and expected
parameter functionality has moved elsewhere.
Other annotations have received slight changes as well, including the common
@Ignored and the lesser known
@Category. All of these have been renamed and given the
same treatment regarding
Assume classes have been renamed as well and are now called
Assumptions. Not much has
changed for the naming of the methods of both classes.
New features: Basics (code)
Here I want to introduce some basics for the new features available in the new version.
There is a new pretty annotation called
@DisplayName which is supposed to improve the readability of test reports, so
you don’t need 40-character test-names to make clear what the test is about at a glance.
You can now also group tests with inner classes annotated with
Assertions and Lambda-Support
Now for the probably most known and anticipated feature in JUnit 5: Lambda-Support…
Assumptions classes and its methods now provide Lambda support. This is achieved by providing
methods with functional interfaces as parameters.
The most used ones are the
Supplier<String>. The first one is used for assertions and the latter
one to provide a result-message. Those are however just alternatives to the older plain
Assertion methods like
assertTrue(...) are now just overloaded with combinations of those four parameters:
BooleanSupplier) & (
Supplier<String>) resulting in 4 different methods. This is what most
lambda-supporting methods are designed like.
A new important functional interface is
Executable. It is very similar to a
Runnable, however it throws a
Throwable meaning you can execute assertions like
assertTrue() and an
AssertionError may be thrown affecting your
test-result. It is used in several assertions like the new
assertAll(Executable... executables) which can be also used
to prevent repetition.
This new functional interface is also used in the new replacement of the old
expected which is
assertThrows(). It asserts whether an exception was thrown.
If you need the exception-instance itself to e.g. assert the message, you can instead use
expectThrows() which also
has the exception as return type.
The biggest new feature in JUnit 5 is the new Extension-API. A part of it is the
ParameterResolver-Interface which is
an extension of the
Extension-Interface itself. The
ParameterResolver-Interface provide a way for dependency
injection on method level by injecting data into test-method parameters.
JUnit 5 provides two implementations by itself:
TestInfo which contains some meta information and the appropriate
Method and Test-
Class instances and
TestReporter which can be used to publish test entries.
A lot more on the Extension-Api is following further below.
New features: Advanced (code)
Building upon the
ParameterResolver paragraph of the last chapter let’s look at implementing your own
ParameterResolver. You can also see the first visual sign of the Extension-API in the form of the
@ExtendWith-Annotation. The final result is:
This is achieved by the following implementations:
The first implementation processes the
className. It checks whether the parameter class is a
String and throws an exception otherwise. To resolve and inject the parameter it just returns the test classes name.
The seconds implementation processes the
parameterIndex. It does basically the same but resolves the
parameter by getting the index from the
Another big feature are the new Test-Factories. These are annotated with
@TestFactory instead of
@Test. Their return
type is some kind of collection of
DynamicTests. The class
DynamicTest provides several static methods to create
those. You basically have to provide test data and based on it a display name as well as some kind of
In my example you can see me using the
stream()-method of said class.
Here I will show you an
Extension that is not based on the
ParameterResolver but instead implements the
TestExecutionCondition. The same thing that powers the
@Disabled annotation. If we want to customize it we need out
own implementation. There are about a dozen of those
TestExecutionCondition is just one of
them. Some are functional interfaces like the one we’re talking about, others like the
ParameterResolver are not.
My example called
@DisabledOnMonday does just that. It disables that test-method or -class on mondays. The
implementation only checks for the weekday and returns an appropriate
ConditionEvaluationResult resulting in the test
being ignored when the weekday matches.
Again this could without problem be placed on class level.
Advanced Test-Samples (code)
Extended disabled weekdays
Let’s extend that
@DisabledOnMonday annotation a bit. What if you want to choose the weekday? Creating 7 annotations
is kind of overkill. A way to achieve this could be to add another annotation that contains the weekdays:
@DisabledWeekdays annotation doesn’t do much more than hold an int array corresponding to the weekdays.
The extension looks slightly different now, since it needs to determine the weekdays from the annotation. Luckily the
evaluate()-method provides the
TestExtensionContext so it’s fairly easy to get those.
So what if you want to save some that space occupied by all those annotations. Let’s make it all-in-one in this example:
What you basically do here is to create a new annotation and annotate that with
@Test. Then you pack all you need in
there like your extensions, parameter resolvers, targets, parameters, etc. The annotation
@UITest above looks like
The extensions used do not really matter here. One extension resolves the
Pane from the fxml path and the other one
just prints some data. This is rather a showcase of an
@Test-Extension and including utilizing the extension features
of JUnit 5. If you want to see code nevertheless look into the repository.
As for the last example right now I will showcase some benchmarking possibilities and it isn’t even that complicated.
There are several extensions that can be used for that.
BeforeTestExecutionCallback and their
After...-equivalents. Each of these interfaces has a method that will be executed at some point during the tests.
E.g. before each test or after etc. So by implementing those 4 interfaces in one extension we can create a class that
timestamps each time a method is called and after it finished including calculating the difference. Then we just need to
annotate an annotation
@Benchmarked with that extension and then place that on top of a test-method or -class. Done.
The final benchmarked test-method will look something like this:
The corresponding test-output:
The extension couldn’t be simpler:
Of course I could have also included
@Benchmarked in a separate
@BenchmarkedTest annotation that would have extended
@Test as well saving that one line.
Milestone 3 Changes (code)
Feel free to express critique and contribute to the repository :)
You can use this repository in any way you want. May it be for workshops or presentations. Just give credits. ;)
- [ ] 5.0 M3 Update - TODO: Update guide
- JUnit 4 interoperability
Additional discovery selectors
- [ ] 5.0 M4 Update - Due by March 5, 2017
- Parameterized tests
- Enhanced dynamic tests
- [ ] 5.0 M5 Update - Due by June 25, 2017
- Scenario tests
- Repeated tests
Test execution in user-defined thread
- [ ] 5.0 RC1 (Release Candidate 1) Update - Due by July 23, 2017
Last fixes before GA
- [ ] 5.0 GA (General Availability Release) Update - Due by August 24, 2017
Official JUnit 5 User Guide
JUnit 5 GitHub
JUnit 5 Milestone plan