Still using JUnit4?

I. Overview

In this tutorial, we'll learn how to migrate from JUnit 4 to the latest JUnit 5 version and cover the differences between the two versions of the library.

For a general guide to using JUnit 5, see our article here.

Second, the advantages of JUnit 5

Let's start with the previous version, JUnit 4, which had some notable limitations:

  • A single jar library contains the entire framework. We need to import the entire library, even if we only need a specific function. In JUnit 5 we get more granularity and can import only what is necessary.
  • In JUnit 4 only one test runner can execute tests at a time (eg SpringJUnit4ClassRunner or Parameterized ). JUnit 5 allows multiple runners to work simultaneously.
  • JUnit 4 never surpassed Java 7 and missed many features of Java 8. JUnit 5 takes full advantage of Java 8 features.

The idea behind JUnit 5 is to completely rewrite JUnit 4 to remove most of these shortcomings.

3. Differences

JUnit 4 is divided into the modules that make up JUnit 5:

  • JUnit Platform -- This module covers all the extension frameworks we might be interested in: test execution, discovery, and reporting.
  • JUnit Vintage -- This module allows backward compatibility with JUnit 4 and even JUnit 3.

3.1. Notes

JUnit 5 made important changes to its annotations. The most important point is that we can no longer use the @Test annotation to specify expectations.

expected parameter in JUnit 4:

@Test(expected = Exception.class)
public void shouldRaiseAnException() throws Exception {
    // ...

Now we can use the method assertThrows:

public void shouldRaiseAnException() throws Exception {
    Assertions.assertThrows(Exception.class, () -> {

timeout property in JUnit 4:

@Test(timeout = 1)
public void shouldFailBecauseTimeout() throws InterruptedException {

Now the assertTimeout method in JUnit 5:

public void shouldFailBecauseTimeout() throws InterruptedException {
    Assertions.assertTimeout(Duration.ofMillis(1), () -> Thread.sleep(10));

Here are some updates in JUnit 5:

  • @Before annotation is now @BeforeEach
  • @After annotation is now @AfterEach
  • @BeforeClass annotation is now @BeforeAll
  • @AfterClass annotation is now @AfterAll
  • @Ignore annotation is now @Disabled

3.2. assertion

We can also write assertion messages in JUnit 5's lambda s to skip complex message construction:

public void shouldFailBecauseTheNumbersAreNotEqual_lazyEvaluation() {
      2 == 3, 
      () -> "Numbers " + 2 + " and " + 3 + " are not equal!");

Additionally, we can group assertions in JUnit 5:

public void shouldAssertAllTheGroup() {
    List<Integer> list = Arrays.asList(1, 2, 4);
    Assertions.assertAll("List is not incremental",
        () -> Assertions.assertEquals(list.get(0).intValue(), 1),
        () -> Assertions.assertEquals(list.get(1).intValue(), 2),
        () -> Assertions.assertEquals(list.get(2).intValue(), 3));

3.3. Assumption

The new Assumptions class is now in org.junit.jupiter.api.Assumptions. JUnit 5 fully supports the existing assumption methods in JUnit 4, and also adds a new set of methods that allow us to run some assertions only in specific scenarios:

public void whenEnvironmentIsWeb_thenUrlsShouldStartWithHttp() {
      () -> {

3.4. Tag and filter

In JUnit 4, we can use the @Category annotation to group tests. In JUnit 5, the @Category annotation was replaced by the @Tag annotation:

public class AnnotationTestExampleTest {

We can include/exclude specific tags using maven-surefire-plugin:


3.5. New annotations for running tests

In JUnit 4, we use @RunWithannotation to integrate test contexts with other frameworks, or to change the overall execution flow in test cases.

In JUnit 5, we can now use the @ExtendWith annotation to provide similar functionality.

For example, to use Spring features in JUnit 4:

  {"/app-config.xml", "/test-data-access-config.xml"})
public class SpringExtensionTest {

In JUnit 5 this is a simple extension:

  { "/app-config.xml", "/test-data-access-config.xml" })
public class SpringExtensionTest {

3.6. New Test Rule Comments

In JUnit 4, we use @Rule and @ClassRule annotations to add special functionality to tests.

In JUnit 5, we can reproduce the same logic using the @ExtendWith annotation.

For example, let's say we have a custom rule in JUnit 4 that writes log traces before and after tests:

public class TraceUnitTestRule implements TestRule {
    public Statement apply(Statement base, Description description) {
        return new Statement() {
            public void evaluate() throws Throwable {
                // Before and after an evaluation tracing here 

We implement it in a test suite:

public TraceUnitTestRule traceRuleTests = new TraceUnitTestRule();

In JUnit 5, we can write the same code in a more intuitive way:

public class TraceUnitExtension implements AfterEachCallback, BeforeEachCallback {

    public void beforeEach(TestExtensionContext context) throws Exception {
        // ...

    public void afterEach(TestExtensionContext context) throws Exception {
        // ...

Using JUnit 5's AfterEachCallback and BeforeEachCallback interfaces provided in the org.junit.jupiter.api.extension package, we can easily implement this rule in our test suite:

public class RuleExampleTest {
    public void whenTracingTests() {

3.7. JUnit 5 Vintage

JUnit Vintage helps migrate JUnit tests by running JUnit 3 or JUnit 4 tests in the context of JUnit 5.

We can use it by importing JUnit Vintage Engine:


4. Conclusion

JUnit 5 is a modular and modern version of the JUnit 4 framework. In this article, we describe the main differences between the two versions and show how to migrate from one version to the other.

A complete implementation of this article can be found at GitHub found in.

Tags: Back-end

Posted by fenderville on Sat, 08 Oct 2022 21:31:22 +1030