Labs ICT
โญ Pro Login

JUnit 4 vs JUnit 5

You might encounter JUnit 4 in older projects, or need to migrate from JUnit 4 to JUnit 5. Understanding the differences between the two versions will help you work with legacy code and appreciate what JUnit 5 brings to the table.

Package Names


// JUnit 4
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import org.junit.Ignore;
import org.junit.Assert;

// JUnit 5
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Disabled;
import static org.junit.jupiter.api.Assertions.*;
    

Side-by-Side Comparison


+-------------------------+------------------+------------------+
|  Feature                |  JUnit 4         |  JUnit 5         |
+-------------------------+------------------+------------------+
|  Test annotation        |  @Test           |  @Test           |
|  Package                |  org.junit       |  org.junit.jupiter|
|  Before each            |  @Before         |  @BeforeEach     |
|  After each             |  @After          |  @AfterEach      |
|  Before all             |  @BeforeClass    |  @BeforeAll      |
|  After all              |  @AfterClass     |  @AfterAll       |
|  Ignore test            |  @Ignore         |  @Disabled       |
|  Expected exception     |  @Test(expected) |  assertThrows()  |
|  Timeout                |  @Test(timeout)  |  @Timeout        |
|  Assertions             |  Assert.*        |  Assertions.*    |
|  Assumptions            |  Assume.*        |  Assumptions.*   |
|  Test runner            |  @RunWith        |  @ExtendWith     |
|  Rules                  |  @Rule           |  Extensions      |
|  Parameterized          |  @RunWith        |  @ParameterizedTest|
|  Display name           |  N/A             |  @DisplayName    |
|  Nested tests           |  N/A             |  @Nested         |
|  Tags                   |  @Category       |  @Tag            |
+-------------------------+------------------+------------------+
    

JUnit 4 Code


import org.junit.*;
import static org.junit.Assert.*;

public class CalculatorTest {

    @BeforeClass
    public static void setUp() {
        System.out.println("Before class");
    }

    @AfterClass
    public static void tearDown() {
        System.out.println("After class");
    }

    @Before
    public void beforeEach() {
        System.out.println("Before each");
    }

    @After
    public void afterEach() {
        System.out.println("After each");
    }

    @Test
    public void addition() {
        assertEquals(5, 2 + 3);
    }

    @Test(expected = ArithmeticException.class)
    public void divideByZero() {
        1 / 0;
    }

    @Test(timeout = 1000)
    public void quickTest() {
        // Must complete within 1 second
    }

    @Ignore("Not implemented yet")
    public void futureTest() {
    }
}
    

Same Code in JUnit 5


import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {

    @BeforeAll
    static void setUp() {
        System.out.println("Before class");
    }

    @AfterAll
    static void tearDown() {
        System.out.println("After class");
    }

    @BeforeEach
    void beforeEach() {
        System.out.println("Before each");
    }

    @AfterEach
    void afterEach() {
        System.out.println("After each");
    }

    @Test
    void addition() {
        assertEquals(5, 2 + 3);
    }

    @Test
    void divideByZero() {
        assertThrows(ArithmeticException.class, () -> {
            int result = 1 / 0;
        });
    }

    @Test
    @Timeout(1)
    void quickTest() {
        // Must complete within 1 second
    }

    @Disabled("Not implemented yet")
    void futureTest() {
    }
}
    

Running JUnit 4 Tests in JUnit 5

The junit-vintage-engine lets you run JUnit 4 tests using the JUnit 5 platform:


<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>5.10.2</version>
    <scope>test</scope>
</dependency>
    

Migration Checklist


1. Add junit-jupiter dependencies
2. Keep junit-vintage-engine temporarily
3. Change package imports
4. Rename: @BeforeClass โ†’ @BeforeAll (make static)
5. Rename: @AfterClass โ†’ @AfterAll (make static)
6. Rename: @Before โ†’ @BeforeEach
7. Rename: @After โ†’ @AfterEach
8. Rename: @Ignore โ†’ @Disabled
9. Replace: @Test(expected=...) โ†’ assertThrows()
10. Replace: @Test(timeout=...) โ†’ @Timeout
11. Replace: Assert.* โ†’ Assertions.*
12. Replace: @RunWith โ†’ @ExtendWith
13. Remove junit-vintage-engine when done
    

Why Upgrade?

  • Better assertions - More informative error messages
  • Exception testing - Inspect the exception, not just its type
  • Nested tests - Organize tests logically
  • Parameterized tests - Built-in, no runner needed
  • Extensions - More powerful than rules
  • Display names - Human-readable test names
  • Tags - Better test filtering
  • Lambda support - Clean, functional-style assertions

๐Ÿงช Quick Quiz

What is the main difference between JUnit 4 and JUnit 5 annotations?