Java’s most popular testing library, JUnit, has released a new version. This version brings several welcome improvements since the last 5.3.2 release. In this blogpost I will go through the most important ones and give code examples where applicable.
As you might know, JUnit 5.x (Jupiter) is a huge improvement over JUnit 4.x (Vintage). JUnit 5 is a more modern version and includes features such as Lambda support, JUnit 5 extensions, test method parameter injection, and many more. JUnit 5.4 is an evolution of JUnit 5, and further improves the testing experience.
Getting started with JUnit 5.4
To get started with JUnit 5.4, we’re using Gradle. Using JUnit 5.4 has been simplified. While in the previous version of JUnit 5, it was necessary to include each module separately (for example, jupiter-api
, jupiter-params
, jupiter-engine
), the new version allows you to using JUnit 5.4 by the inclusion of just the single junit-jupiter
dependency-aggregating artifact.
Dependency inclusion in 5.3:
testCompile "org.junit.jupiter:junit-jupiter-api:5.3.0"
testCompile "org.junit.jupiter:junit-jupiter-params:5.3.0"
testRuntime "org.junit.jupiter:junit-jupiter-engine:5.3.0"
Can now be simplified in 5.4+ to:
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.4.2'
To use JUnit 5 in your project, add the following line to your build.gradle
file:
test {
useJUnitPlatform()
}
@TempDir support for temporary directories
While testing, sometimes you need to get access to a temporarily file. Instead of handling the creation of such a file yourself, JUnit 5 now provides an extension to create and cleanup temporary files. You can use it putting the @TempDirectory
annotation or Path
or File
test input parameters or fields. An example:
@Test
void useTempDir(@TempDir Path tempDir) throws IOException {
Path file = tempDir.resolve("hello.txt");
Files.write(file, "hello".getBytes());
assertEquals(asList("hello"), Files.readAllLines(file));
}
Parameterized test null and empty support
Parameterized tests are a great way to to provide input to test methods. An example can be seen below:
@ParameterizedTest
@EnumSource(CountryCode.class)
void checkCountryCode(CountryCode countryCode) {
assertTrue(countryChecker.isValid(countryCode));
}
In a setup like this it’s a bit harder to supply a null element or an empty element. @NullSource
(and @EmptySource
plus @NullAndEmptySource
) to the rescue.
@ParameterizedTest
@NullSource // now also null is tested
@EnumSource(CountryCode.class)
void checkCountryCode(CountryCode countryCode) {
assertTrue(countryChecker.isValid(countryCode));
}
Test display name is now smarter
Normally, when having test classes, it’s possible to override the test method name or class by using the @DisplayName
annotation. For an example, see below.
@Test
@DisplayName("Apply 10% discount on products above $100")
public void applyDiscount() { }
While the above provides a more readable format, it’s quite static. What you can do instead is generate the display name based on the nested class or the method name. A good DisplayNameGeneration example can be found in the documentation.
How to order JUnit 5 test methods?
Generally it’s not a great idea to have test methods depend on each other. However, for some scenarios it’s handy to have a certain sequence in the test execution. For example, one test might create a resource in a REST endpoint. Subsequent tests can verify certain attributes of this resource.
In the previous versions of JUnit, this was hard to do, but since 5.4, you can use a new TestMethodOrder
, named OrderAnnotation
. You can use this annotation in combination with with the Order
annotation to force the execution of the test methods in a specific order.
@TestMethodOrder(OrderAnnotation.class)
class SequenceTest {
@Test
@Order(1)
void createResource() {
// Create a resource first...
}
@Test
@Order(2)
void verify() {
// ...then verify some attributes.
}
}
What’s next?
There are many other features available in the new JUnit 5 release, which can be found in the release notes. One of the great things about JUnit 5, besides the framework, is that the documentation is of an amazing quality. There is a lot of documentation available, in which concepts are explained with very illustrative code examples.
If you haven’t already, it’s time to upgrade to JUnit 5. The latest release provides a lot of great features which will make migration worth it.