Hi there. It’s been a while, but we finally caught up with the current Virtual JUG sessions and in this blog we present a recap of the wonderful and very practical presentation: JUnit 5: Next Generation Testing on the JVM.

For this presentation, the speaker was none other than Nicolai Parlog, a JVM aficionado, a software engineer with tons of experience, and a frequent conference speaker and blogger. You can find Nicolai’s blog at codefx.org and I can promise you’ll learn a lot.

Nicolai constantly reads, thinks, and writes about the software development, and codes for a living as well as for fun, so he has the expertise to share with the world.

In any case, before we proceed, be sure to follow Nicolai on Twitter: @nipafx and if something remains unclear in the session ping him and he’d be happy to chat with you!

JUnit 5: Next Generation Testing on the JVM

Well, if for the last ten years you haven’t been living under a rock, or not on the JVM, you probably have at least some experience with JUnit. You’ll know it’s the most used library to write unit tests in for Java projects.

JUnit is quite mature and is pretty good as libraries go, however recently, after the release of the JDK 8, the JUnit team worked hard and delivered a major rewrite of the test engine. One of the main syntactical reasons was to adopt lambdas, so you can write code that is full of them and benefit from unit tests that use a similar code style. However, the differences between JUnit version 4 (or older, if you’re still half under the rock) and the fresh version 5 do not stop there.

The Basics

The most basic test using JUnit 5 can look something like this:

class JUnit5Test {
@Test
void someTest() {
assertTrue(true);
}
}

If you’ve dealt with the JUnit before the first thing you notice that now you don’t have to declare everything as public, the default visibility suffices for the tests. This can help you by allowing you to better structure your tests as well as not importing them by mistake into your production code!

Another minor, but pleasant change is the renamed lifecycle methods for setting up the fixtures and setting up the tests:

Then we have one of the major changes to the core functionality of JUnit. The assertions methods have now been rewritten to accept lambdas for the message creation. And the best part is that you can group several assertions together and evaluate them together. This will capture all the assertion errors and report them together rather than stop after the first error.

Isn’t that cool? Now you’ll have even more goodness with JUnit 5. The @Nested annotation will allow you to better control the order of your test executions. On top of that you now have the ability to control the reporting names of your test cases. Use the @DisplayName annotation to specify what you want to see in the report for that method.

Dynamic tests

Next, Nicolai showed some more advanced features that can make your testing much easier. One problem with the JUnit 4 and older libraries was that you had to specify all the tests during compile time. You needed to declare all the methods and their assertions in the code.

JUnit 5 supports the dynamic test declaration. It offers an API to create the test cases on the fly, at runtime and you can use it, for example to fetch the data for the test cases from an external location.

To create dynamic tests you need to annotate a method with the @TestFactory annotation and make it return a collection (or a stream, or an iterable, JUnit is pretty flexible) of type DynamicTest.

Note that the code inside the dynamic test, that you can specify as a lambda, you’ll use the default assertion method and will have all the support that JUnit provides for the normal compile time tests.

Extensions

One of the main JUnit 5 principles is: prefer extension points over features. And to support the use of the extension points, the JUnit team has introduced another annotation: @ExtendWith.

JUnit 5 provides a comprehensive list of extensions points that you can use straight out of the box, including:

Test Instance Post Processor

BeforeAll Callback

Test and Container Execution Condition

BeforeEach Callback

Parameter Resolution

Before Test Execution

After Test Execution

Exception Handling

AfterEach Callback

AfterAll Callback

Imagine for example that you want to report the execution time for each test. You’ll need to implement an extension:

Your extension will implement the necessary callbacks to receive the control flow at the right times of the test execution. Then you can go wild with whatever code you want to implement. The example above simply captures two timestamps, before and after the test and reports the difference as the time it took to execute the test.

You can also implement all kinds of conditional executions. A great example of this is the @Disabled annotation that prevents JUnit from executing the test in question.

Then you just slap the @ExtendWith annotation on your test class or method and it will make JUnit engine to call your extension when needed. One particularly useful application of this is the parameter resolvers: you can specify the parameters for your test methods and they will be provided from the general test context.

To get the exact details on how to use them, you should take your time and watch the session in full. Nicolai did a wonderful job at explaining all the gritty details, provided tons of examples, and in general presented the material very well.

You’ll also learn a thing or two about the new architecture of the JUnit project, how the test engine is different from what we had earlier and what are the best ways to set up the project using JUnit or how to properly enable JUnit in the continuous integration environments.

Useful links to the resources

Here are the links to the resource Nicolai mentioned in the session and a couple we found quite useful afterwards.

A friendly chat with Nicolai

After most vJUG sessions we interview the speaker and ask them more questions about the general state of affairs on the JVM, in the community, and what’s the best way to learn the topic they are proficient at. Nicolai is no exception, he took time to sit with us and now you can enjoy the result of that chat too. If you have any further questions or just want to say thanks to Nikolai, ping him on Twitter.

Oleg Šelajev is an engineer, author, speaker, lecturer and advocate at ZeroTurnaround. He spends his time testing, coding, writing, giving conference talks, crafting blogposts and reports. He is pursuing a PhD on Dynamic System updates and code evolution. Oleg is a part-time lecturer at the University of Tartu and enjoys speaking and participating in Java/JVM development conferences such as JavaOne, JavaZone, JFokus and others. In his free time, Oleg plays chess at a semi-grandmaster level, loves puzzles and solving all kinds of problems.