Unit Testing (JUnit)
Manually running a program and eyeballing its output doesn't scale — it's slow, it's easy to forget a case, and it gives you no protection against a future change quietly breaking something that used to work. Automated tests fix this: you write code that exercises your code and checks the result, and you can rerun that check in seconds, as often as you like, for the entire life of the project.
Writing a Test
Calculator.java
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Cannot divide by zero");
}
return a / b;
}
}CalculatorTest.java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
void addsTwoNumbers() {
Calculator calculator = new Calculator();
assertEquals(5, calculator.add(2, 3));
}
@Test
void divideByZeroThrows() {
Calculator calculator = new Calculator();
assertThrows(IllegalArgumentException.class, () -> calculator.divide(10, 0));
}
}Common Assertion Methods
Method | Purpose |
assertEquals(expected, actual) | Fails unless the two values are equal |
assertTrue(condition) | Fails unless the given boolean expression is true |
assertThrows(Type.class, () -> ...) | Fails unless the supplied code throws an exception of the given type |
assertNotNull(value) | Fails if the value is null |
Setup and Teardown with @BeforeEach / @AfterEach
Many tests need the same setup — a fresh object, a database connection, a temporary file — before each test runs, and often some cleanup afterward. Rather than repeating that code in every test method, JUnit lets you factor it into lifecycle methods that run automatically around each test.
Sharing setup across tests
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CalculatorLifecycleTest {
private Calculator calculator;
@BeforeEach
void setUp() {
calculator = new Calculator(); // runs before every @Test method
}
@AfterEach
void tearDown() {
calculator = null; // runs after every @Test method
}
@Test
void addsTwoNumbers() {
assertEquals(7, calculator.add(3, 4));
}
}@BeforeEach runs before every test method in the class — ideal for creating a fresh object under test
@AfterEach runs after every test method — useful for closing resources opened during setup
@BeforeAll / @AfterAll (static methods) run once for the whole class, for expensive one-time setup