JUnit
JUnit 5 (Jupiter): the modern Java test framework. Annotations, parameterised tests, lifecycle, assertions, extensions.
Java — JUnit 5
EXAMPLE
// ===== Setup (Maven) =====
// pom.xml
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
// ===== Basic test =====
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class MathTest {
@Test
void onePlusOne() {
assertEquals(2, 1 + 1);
}
}
// Run: mvn test or gradle test
// ===== Annotations =====
// @Test regular test
// @DisplayName human-readable label
// @BeforeEach runs before each test
// @AfterEach runs after each test
// @BeforeAll runs once before all tests (static)
// @AfterAll runs once after all tests (static)
// @Disabled skip
// @Nested nested test class
// @Tag("slow") group + filter
@DisplayName("User service")
class UserServiceTest {
UserService svc;
@BeforeEach
void setUp() {
svc = new UserService(new InMemoryRepo());
}
@Test
@DisplayName("creates new user")
void createsUser() {
var user = svc.create("Alex");
assertNotNull(user.id());
assertEquals("Alex", user.name());
}
}
// ===== Assertions =====
assertEquals(expected, actual);
assertEquals(expected, actual, "helpful message");
assertNotEquals(2, 3);
assertTrue(condition);
assertFalse(condition);
assertNull(value);
assertNotNull(value);
assertSame(o1, o2); // reference equality
assertArrayEquals(new int[]{1,2,3}, result);
// Iterables:
assertIterableEquals(List.of(1, 2, 3), result);
// Multiple assertions in one test:
assertAll("user",
() -> assertEquals("Alex", user.name()),
() -> assertNotNull(user.id())
);
// Exceptions:
var ex = assertThrows(IllegalArgumentException.class, () -> svc.create(null));
assertEquals("name required", ex.getMessage());
// Timeouts:
assertTimeout(Duration.ofSeconds(1), () -> longOp());
assertTimeoutPreemptively(Duration.ofMillis(500), () -> blockingOp());
// ===== Parameterised tests =====
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
class ParityTest {
@ParameterizedTest
@ValueSource(ints = {2, 4, 6, 8})
void allEven(int n) {
assertEquals(0, n % 2);
}
@ParameterizedTest
@CsvSource({
"1, 2, 3",
"4, 5, 9",
})
void adds(int a, int b, int expected) {
assertEquals(expected, a + b);
}
static Stream<Arguments> data() {
return Stream.of(
arguments("a", 1),
arguments("b", 2)
);
}
@ParameterizedTest
@MethodSource("data")
void parametricFromMethod(String name, int n) { /* ... */ }
}
// ===== Nested tests =====
class CartTest {
@Nested class WhenEmpty {
@Test void totalIsZero() { /* ... */ }
}
@Nested class WhenHasItems {
@Test void totalSums() { /* ... */ }
}
}
// ===== Mocking (Mockito) =====
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
@Test
void usesRepo() {
var repo = mock(UserRepo.class);
when(repo.findById(1)).thenReturn(new User(1, "Alex"));
var svc = new UserService(repo);
assertEquals("Alex", svc.greet(1));
verify(repo).findById(1);
}
// ===== Extensions =====
// Custom behaviour via @ExtendWith. Spring Boot has @SpringBootTest.
// ===== Patterns =====
// - One test class per production class
// - @Nested for grouping by scenario
// - assertAll for multi-property checks
// - assertThrows for error testing
// - Parameterised tests for boundary conditions
// ===== Pitfalls =====
// - Static state across tests -> flaky tests
// - Test order assumptions (do not rely on it)
// - Heavy @BeforeAll with shared state
// - Mocking the class under test instead of its dependencies
Why it matters
JUnit 5 is the modern Java test framework. @Test, @DisplayName, @ParameterizedTest, @Nested, assertAll, assertThrows. Pair with Mockito for mocks. Keep tests independent, label them well, parameterise for breadth, and the suite stays maintainable as the codebase grows.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Discussion
Loading…