A test method is annotated @Test and calls assertion methods (assertEquals, assertTrue, assertThrows) to verify the code under test behaves as expected; a failed assertion throws, and the test runner reports that method as failed. JUnit 6, released in September 2025 (eight years after JUnit 5), is the current generation, requiring Java 17 or later and unifying the versioning that JUnit 5 had split across separate packages — the annotation-based testing style itself carries over largely unchanged from JUnit 5's Jupiter API, so existing knowledge transfers directly.
A basic test
assertEquals reports both the expected and actual values on failure, which is far more useful for debugging than a bare boolean assertTrue would be.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
void addsTwoNumbers() {
Calculator calc = new Calculator();
assertEquals(5, calc.add(2, 3));
}
@Test
void throwsOnDivideByZero() {
Calculator calc = new Calculator();
assertThrows(ArithmeticException.class, () -> calc.divide(1, 0));
}
}Setup and teardown: @BeforeEach, @AfterEach
@BeforeEach runs before every single test method in the class, useful for setting up a fresh instance each time so tests don't accidentally share state and affect each other.
class UserServiceTest {
private UserService service;
@BeforeEach
void setUp() {
service = new UserService(); // fresh instance before EVERY test method
}
@Test
void createsUser() {
service.create("Alice");
assertEquals(1, service.count());
}
}Parameterized tests: one test, many inputs
@ParameterizedTest runs the same test logic once per value supplied, avoiding copy-pasting nearly identical test methods for each input case.
@ParameterizedTest
@ValueSource(ints = { 2, 4, 6, 8 })
void isEven(int number) {
assertTrue(number % 2 == 0); // runs once per value in the list, all as separate test results
}