You already saw @DisplayName in previous lessons, but let us dive deeper
into how to use it effectively and explore other naming features in JUnit 5. Good
test names are one of the most underrated aspects of testing.
Why Test Names Matter
Imagine looking at this test report:
Tests run: 15
- test1 PASSED
- test2 FAILED
- test3 PASSED
- test4 PASSED
- test5 PASSED
Now imagine this one:
Tests run: 15
- "Should add two positive numbers" PASSED
- "Should throw exception for negative price" FAILED
- "Should return empty list when no items exist" PASSED
- "Should format currency with two decimals" PASSED
- "Should reject null user name" PASSED
The second report tells you exactly what is broken and what the intention was. Test names are documentation.
@DisplayName in Action
import org.junit.jupiter.api.*;
@DisplayName("Calculator Operations")
class CalculatorTest {
Calculator calc = new Calculator();
@Test
@DisplayName("Addition: should return sum of two positive numbers")
void addPositiveNumbers() {
assertEquals(5, calc.add(2, 3));
}
@Test
@DisplayName("Addition: should handle negative numbers correctly")
void addNegativeNumbers() {
assertEquals(-1, calc.add(-3, 2));
}
@Test
@DisplayName("Division: should throw ArithmeticException when dividing by zero")
void divideByZero() {
assertThrows(ArithmeticException.class,
() -> calc.divide(10, 0));
}
@Nested
@DisplayName("Multiplication Tests")
class MultiplicationTests {
@Test
@DisplayName("Should multiply two positive numbers")
void multiplyPositive() {
assertEquals(6, calc.multiply(2, 3));
}
@Test
@DisplayName("Should return zero when multiplied by zero")
void multiplyByZero() {
assertEquals(0, calc.multiply(5, 0));
}
}
}
Naming Conventions
Here are some popular naming styles. Pick one and stick with it:
// 1. Behavior-driven (recommended)
@DisplayName("Should [expected behavior] when [condition]")
void shouldReturnSumWhenGivenTwoNumbers() { }
// 2. Given-When-Then
void givenValidUser_whenLogin_thenReturnsToken() { }
// 3. Method-Under-Test style
void add_withPositiveNumbers_returnsSum() { }
// 4. Simple descriptive
void additionOfTwoNumbers() { }
// 5. BDD style with underscores
void login_with_valid_credentials_returns_token() { }
The key principle: when a test fails, the name should tell you what went wrong and what the expected behavior was.
Combining @DisplayName with @Nested
The real power of @DisplayName shows when combined with @Nested:
@DisplayName("User Registration")
class RegistrationTest {
@Nested
@DisplayName("when email is valid")
class ValidEmail {
@Test
@DisplayName("should register successfully")
void registerSuccessfully() {
// ...
}
@Test
@DisplayName("should send confirmation email")
void sendsConfirmationEmail() {
// ...
}
}
@Nested
@DisplayName("when email is invalid")
class InvalidEmail {
@Test
@DisplayName("should reject registration")
void rejectsRegistration() {
// ...
}
@Test
@DisplayName("should return descriptive error message")
void returnsErrorMessage() {
// ...
}
}
}
This produces a beautifully structured test report that reads like documentation:
User Registration
โโโ when email is valid
โ โโโ should register successfully PASSED
โ โโโ should send confirmation email PASSED
โโโ when email is invalid
โโโ should reject registration PASSED
โโโ should return descriptive error message FAILED
Tips for Good Test Names
- Use complete sentences with @DisplayName
- Start with "Should" for assertions
- Include the scenario and expected outcome
- Use @Nested to group related tests by context
- Avoid vague names like "test1", "test2", or "myTest"
- Do not repeat the class name in every test name