π§
Modern Java
Modern Java Β· Prerequisite: previous lecture
22. JUnit 5 Unit Testing
Saying "the code works" requires **verification**. JUnit 5 is the de-facto standard test framework for Java, with a concise annotation-driven API.
JavamodernJUnitunit test
Duration
β± ~1.5-2 hours
Level
π Intermediate-Advanced
Prerequisite
π― Previous lecture or equivalent knowledge
OUTCOME
Saying "the code works" requires **verification**. JUnit 5 is the de-facto standard test framework for Java, with a concise annotation-driven API.
What you'll learn
- 1Know where `src/test/java` lives in a Maven project
- 2Use `@Test`, `@BeforeEach`, `@AfterEach`, `@DisplayName`
- 3Distinguish `assertEquals` / `assertTrue` / `assertThrows`
- 4Run tests with `mvn test`
Overview
Saying "the code works" requires **verification**. JUnit 5 is the de-facto standard test framework for Java, with a concise annotation-driven API.
Core Concepts
1) Maven dependency
xml
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.5</version>
<scope>test</scope>
</dependency>`scope=test` keeps it out of production artifacts.
2) Basic test
java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
void add() {
assertEquals(5, 2 + 3);
}
}3) Lifecycle annotations
| Annotation | When it runs |
|---|---|
| `@BeforeAll` | once before all tests (static) |
| `@BeforeEach` | before every test |
| `@Test` | a test method |
| `@AfterEach` | after every test |
| `@AfterAll` | once after all tests (static) |
4) Common assertions
java
assertEquals(expected, actual);
assertTrue(condition);
assertNotNull(value);
assertThrows(IllegalArgumentException.class, () -> ...);
assertAll("group", () -> assertEquals(1, a), () -> assertEquals(2, b));5) Run it
bash
mvn test
# or in IntelliJ: Right-click β Run 'CalculatorTest'Examples
Example 1 β `CalculatorTest.java`
java
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
Calculator c;
@BeforeEach
void setUp() { c = new Calculator(); }
@Test
@DisplayName("adds two numbers")
void add() {
assertEquals(5, c.add(2, 3));
}
@Test
void divByZero() {
assertThrows(ArithmeticException.class, () -> c.divide(1, 0));
}
}
class Calculator {
int add(int a, int b) { return a + b; }
int divide(int a, int b) { return a / b; }
}Example 2 β Lifecycle
java
import org.junit.jupiter.api.*;
class LifecycleTest {
@BeforeAll static void initAll() { System.out.println("before all"); }
@BeforeEach void init() { System.out.println("before each"); }
@Test void one() { System.out.println("test one"); }
@Test void two() { System.out.println("test two"); }
@AfterEach void cleanup() { System.out.println("after each"); }
@AfterAll static void cleanupAll() { System.out.println("after all"); }
}**Output**
text
before all
before each
test one
after each
before each
test two
after each
after allExample 3 β `assertAll`
java
@Test
void grouped() {
assertAll("person",
() -> assertEquals("Jisoo", person.name()),
() -> assertEquals(21, person.age())
);
}Example 4 β `assertThrows` with message check
java
@Test
void rejectsZero() {
var ex = assertThrows(IllegalArgumentException.class, () -> safeDiv(1, 0));
assertEquals("b != 0", ex.getMessage());
}Common Mistakes
- Forgetting `<scope>test</scope>` so test code leaks into production
- Marking `@BeforeAll` / `@AfterAll` non-static β JUnit refuses to run them
- Storing state in fields without `@BeforeEach` resets β tests become order-dependent
- Using `assertEquals(a, b)` when you meant `assertSame` for reference equality
- Writing one test method that asserts twenty things β split per concern
Summary
- `@Test` + assertions are the minimum
- `@BeforeEach` / `@AfterEach` keep tests isolated
- Run with `mvn test` (or your IDE)
Practice
# Practice - 22. JUnit 5
## Exercise 1 β Test `isPrime`
- File: `IsPrimeTest.java`
- Key concepts: `@Test`, `assertTrue`/`assertFalse`
Requirements
- Write `isPrime(int)`.
- Tests: 2, 3, 5, 7, 11 β true; 1, 4, 6, 8, 9 β false.
## Exercise 2 β Test exception path
- File: `DivideTest.java`
- Key concepts: `assertThrows`
Requirements
- Write `divide(a, b)` that throws `IllegalArgumentException` if `b == 0`.
- Verify the exception and its message.
## Solutions After trying it yourself, compare with [`answer/`](./answer/).
Solution code (homework/answer/)
answer/IsPrimeTest.java
java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class IsPrimeTest {
static boolean isPrime(int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; i++) if (n % i == 0) return false;
return true;
}
@Test void primes() {
for (int n : new int[]{2, 3, 5, 7, 11}) assertTrue(isPrime(n));
}
@Test void composites() {
for (int n : new int[]{1, 4, 6, 8, 9}) assertFalse(isPrime(n));
}
}
answer/DivideTest.java
java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class DivideTest {
static int divide(int a, int b) {
if (b == 0) throw new IllegalArgumentException("b != 0");
return a / b;
}
@Test void normal() { assertEquals(5, divide(10, 2)); }
@Test void zeroDenominator() {
var ex = assertThrows(IllegalArgumentException.class, () -> divide(1, 0));
assertEquals("b != 0", ex.getMessage());
}
}
Try It Yourself
bash
mvn testNext Lecture
[23_Spring_Boot](../../06_Spring_Boot/23_Spring_Boot_μμνκΈ°/) β chapter 6 begins: getting started with Spring Boot.
Example code / lecture materials
All lecture materials and example code are openly available on GitHub.
View on GitHub β