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