Difference between Junit 4 and Junit 5
Key Differences
Feature JUnit 4 JUnit 5 (a.k.a. Jupiter)
Release Year 2006 2017
Architecture Monolithic Modular (Jupiter, Platform, Vintage)
Test Engine Single Multiple engines supported
Annotations @Before, @After, @BeforeEach, @AfterEach,
@BeforeClass, @AfterClass, @BeforeAll, @AfterAll, @Test
@Test
Test Instance No control @TestInstance(PER_CLASS)
Lifecycle available
Assertions Basic JUnit + Hamcrest Modern assertions + AssertJ
support
Exception @Test(expected = ...) assertThrows()
Testing
Timeouts @Test(timeout = ...) @Timeout(2)
Disabled @Ignore @Disabled
Tests
Extensions/ @RunWith(MockitoJUnitRunn @ExtendWith(MockitoExtensi
Mocks er.class) on.class)
Parameterize JUnitParams or custom runners Built-in via @ParameterizedTest
d Tests
IDE + Tool Good Excellent (modular + extensible)
Integration
Legacy N/A Supports running JUnit 4 via
Support Vintage
Tagging Not available @Tag("integration") etc.
Tests
Annotations Differences
Purpose JUnit 4 JUnit 5
Test Method @Test @Test
Before each test @Before @BeforeEach
After each test @After @AfterEach
Before all tests @BeforeClass (static) @BeforeAll (static)
After all tests @AfterClass (static) @AfterAll (static)
Ignore test @Ignore @Disabled
Expected @Test(expected = ...) assertThrows(...)
exception method
Timeout for test @Test(timeout = ...) @Test,
assertTimeout(...)
Parameterized @RunWith(Parameterized.class) @ParameterizedTest
tests
Test names N/A @DisplayName
(display)
Tagging N/A @Tag("unit")
CalculatorTest using JUnit 5
CalculatorTest using JUnit 4
BankService Junit 4 testing example:
BankService.java
public class BankService {
public double transferFunds(double fromBalance, double amount) {
if (amount <= 0) throw new IllegalArgumentException("Transfer amount must be positive");
if (fromBalance < amount) throw new IllegalStateException("Insufficient balance");
return fromBalance - amount;
}
public void simulateLongOperation() throws InterruptedException {
Thread.sleep(2000); // Fake delay
}
}
BankServiceTest.java
import org.junit.*;
import static org.junit.Assert.*;
public class BankServiceTest {
private BankService bankService;
@BeforeClass
public static void beforeAllTests() {
System.out.println("Before ALL tests – runs once");
}
@AfterClass
public static void afterAllTests() {
System.out.println("After ALL tests – runs once");
}
@Before
public void setUp() {
bankService = new BankService();
System.out.println("Before each test");
}
@After
public void tearDown() {
System.out.println("After each test");
}
@Test
public void testSuccessfulTransfer() {
double remainingBalance = bankService.transferFunds(1000, 300);
assertEquals(700, remainingBalance, 0.001);
}
@Test(expected = IllegalArgumentException.class)
public void testTransferNegativeAmountThrowsException() {
bankService.transferFunds(1000, -100);
}
@Test(expected = IllegalStateException.class)
public void testInsufficientBalanceThrowsException() {
bankService.transferFunds(200, 500);
}
@Ignore("This test is ignored just to demonstrate @Ignore")
@Test
public void testIgnoredTransferCase() {
fail("This should not run!");
}
@Test(timeout = 3000) // fails if it takes more than 3 seconds
public void testSimulateLongOperation() throws InterruptedException {
bankService.simulateLongOperation();
}
}