7 Reasons to Consider JUnit 5

DZone 's Guide to

7 Reasons to Consider JUnit 5

JUnit 5 will be out soon! With the new Jupiter API, a focus on extensibility, and a huge integration overhaul, it promises to make testing easier than ever.

· Java Zone ·
Free Resource

The latest version of JUnit is just around the corner. According to the official roadmap the final release is planned for Q3 2017. At the moment of writing, 4 milestones have been published and a few more are planned before release candidates, but it doesn’t mean you have to wait several months to make use of JUnit 5. The goal of this post is to encourage you in seven points to start playing with the new version the framework right away.

1. Ready to Use

It’s not unusual, when a new version of language, application server, or library comes to light, that developers tend to postpone learning to a time when the industry is ready to adopt the changes. Just look at JavaScript. Two years after the release of the ES2015 specification, web browsers are still struggling to support all language features. Transpilers did a great job to help with popularization but, as a side effect, introduced an additional step in the development process.

The JUnit 5 team facilitates the adoption of the new version from the beginning. IntelliJ IDEA already runs tests natively within the IDE. Eclipse introduced its beta support as well. The two most popular build tools, Maven and Gradle, handle JUnit 5 tests without issue. Moreover, if you use some less popular tool, a console launcher will help you to execute tests automatically. Last but not least, there is also a dedicated JUnit 4 Runner that allows running new tests in environments that support only the previous version. As you can see, JUnit 5 gives plenty of choices.

2. Easy to Learn

At the first glance, the new API (also known as JUnit Jupiter) strongly resembles its predecessor. The level of similarity is so high that you might even think that creating a new major release of the framework for such cosmetic changes is a considerable abuse. The most popular assertions are still the same. The only differences are in the package they are placed and the order of parameters in overloaded variants. Widely known annotations changed their names but are still responsible for the same actions. If you are familiar with JUnit 4 you can grasp the basics of JUnit 5 within a few minutes.

At this point, you may ask, if it is so similar to the previous version, then why you should bother? In short, JUnit 5 is like a superset of the predecessor and has much more to offer.

3. Brand New Features

Once you familiarize yourself with the basics (approx. 15 minutes) you can move to the next level. Depending on your needs, you should consider experimenting with:

  • New assertions.
  • Nested test classes: interesting not only for Behavior Driven Development followers
  • Dynamic tests: generating test cases at runtime
  • Test extensions: allowing you to redefine the behavior of tests and test classes in a reusable and pluggable way

4. Solving JUnit 4's Problems

Nothing is perfect, including JUnit 4. There are several known different size issues that have been worked around by the community. Let’s quickly look over these problems.

4.1. Exception Verification

The flagship example of all aforementioned issues is exception checking. Consider the following JUnit 4 test:

@Test(expected = IllegalArgumentException.class)
public void shouldThrowException() throws Exception {
    Task task = buildTask();
    LocalDateTime oneHourAgo = LocalDateTime.now().minusHours(1);

Imagine we want to test if the execute() method of the task object throws the IllegalArgumentException if the given argument is in the past. The test looks fine, but what happens if the exception is thrown by the buildTask() method? The test will give us a false positive. We should check if the exception occurs exactly in the place where it is expected, not the whole test body. JUnit 5 gives you assertThrows() out-of-the-box, which gracefully handles the described problem as in the following snippet:

void shouldThrowException() throws Exception {
    Task task = buildTask();
    LocalDateTime oneHourAgo = LocalDateTime.now().minusHours(1);
                 () -> task.execute(oneHourAgo));

4.2. Timeout Measurement

In addition, a similar set of assertions has been provided to verify timeouts in the code provided within a lambda expression. You don’t have to worry that the setup part of a test will have an impact on the measured execution time. Now you can choose which code is measured. Also, there is an option to stop execution when the timeout is exceeded or to continue and measure the actual time required to execute tested code.

void shouldTimeout() throws Exception {
    ExpensiveService service = setupService();
    assertTimeout(ofSeconds(3), () -> {

4.3. Parameterized Tests

If you don’t like the fact that JUnit4 requires using test class fields for parameterized tests, the new approach proposed in JUnit 5 will surely meet your needs. Parameters are now connected directly to a test method and nothing prevents you from putting different parameterized tests in a single class, which isn’t possible in JUnit 4.

Test parameters can be supplied by an array of sources, including CSV-like strings, external CSV files, enums, factory methods, and dedicated provider classes. String literals are automatically converted into numerous types and you can also provide your custom converters if the built-in ones don’t cover your use cases.

4.4. Multiple Runners

Probably the greatest issue of JUnit 4 is its inability to use several test runners in a single class. Limitations of parameterized tests or nested test classes can be solved with runners, but they cannot be used with others. We are forced to pick just one. For instance, before Spring 4.2, every test depended on your application context had to use the SpringJUnit4ClassRunner. Now you can use a dedicated rule to work around the issue so you can use a different runner, but still only one. JUnit 4 wasn’t designed with extensibility in mind and it pays the price of backward compatibility. The JUnit 5 team has chosen a different path and planned the architecture of the new version to solve this problem thanks to the already mentioned extensions.

5. Simple Migration

Do you already have hundreds of tests in your current project and shivering at the thought of rewriting them? Relax. The JUnit 5 team thought about different options for a migration. First things first, JUnit 4 and 5 can both be used in one project without conflicts. Once you add the new version to a project’s dependencies, you have two alternatives for running tests together.

The least invasive option is to write new tests with the JUnit 5 API and run them with a dedicated JUnitPlatform runner for JUnit 4. But wait, didn’t we just say those runners are the crux of the matter? Well, the runner has its limitations and supports only a subset of features introduced by the new version. But it’s good enough to start using the latest API in a living project. The fastest way to learn is to practice on a real problem, right?

The second alternative is to promote JUnit 5 as a master runner of all tests. Besides all the best features of the framework, you will get the ability to execute old tests within the new platform, but with one little reservation. Because of the conceptual differences, only selected rules from JUnit 4 are supported in JUnit 5. Before you take a leap of faith into the JUnit 5 platform, spend some time to familiarize yourself with the limitations.

6. Opportunity to Contribute

Since the new version is still under development, there is no better moment to request a feature that might be useful. It is you, the user of the framework, who knows best what is expected from a dependency you put into your project. Every reasonable idea may be reported in the issue tracker of the JUnit 5 project.

But before you open a new ticket, make sure that the framework doesn’t already have what you are looking for. Don’t waste the time of the team for issues that have already been solved or don’t add much value. However, if you have a good proposal, don’t hesitate and write a descriptive overview. No one said that you can contribute to open source only by writing code. Ideas also matters. All issues can be found here.

7. Future of JVM Testing

JUnit 5 isn’t just another pretender to the testing framework throne. The architecture of JUnit 5 has been greatly altered — not only because of the breaking changes in the public API and the introduction of the extension model. One of the goals was to create a base platform for JVM testing frameworks. What does that mean? Let’s take a glance at the following diagram.

As you can see, just like onions and ogres, JUnit 5's architecture has layers. The core of the framework is the platform. IDEs and build tools as clients communicate with the platform in order to execute tests in a project. Available TestEngine implementations called by the platform discover and run tests, and the unified output is reported through the platform back to clients.

The key point is extensibility, but not at the test class level as mentioned before. It is extensibility at the level of the whole testing platform. Any framework can run its tests on the JUnit platform as long as it provides an implementation of the TestEngine interface. With a little effort, by covering this single integration point, that imaginary framework will get the support of all IDEs and build tools that are already integrated with the platform. The barriers to entering the industry for newly invented frameworks will be much lower because they won’t have to provide support for aforementioned IDEs and tools.

But what do all these changes give you as a developer? It should be much easier to convince your manager, lead developer, or whoever blocked your last suggestion to experiment with an exotic testing framework that said framework has integration with all major tools on the JVM development market. JUnit Vintage, which executes old JUnit 4 tests, is a prime example. This is a proven concept.

Not Convinced Yet?

Even if you are not an early adopter type of developer, you should still keep an eye on the changes introduced in the newest version of JUnit. With all the advantages that JUnit 5 brings and the fame of the predecessor, its way into the mainstream is just a matter of time. For those who don’t want to experiment on their real applications, a side project might be a great option to get to know with the new JUnit Jupiter API. If you find any issue or have a comment, feel free to report it. Open source is created by the whole community, not only by the core project development team. It is up to you whether you want to become a contributor.

java, junit 5, jvm testing, unit testing

Published at DZone with permission of Daniel Olszewski . See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}