Phase 18
JUnit 5, Mockito & Test-Driven Development (TDD) Architecture
📂 Module 3_ Unit Testing with Junit 5 \ 01_Junit5_Demos & 01_TDD_Demos
18.1 JUnit 5 Architecture: Parameterized Tests, Nested Lifecycles & Assertions
📖 Cay S. Horstmann • Core Java Volume I — Chapter 7 ("Unit Testing and Assertions")
“Software defects must be detected automatically before software reaches production. JUnit 5 separates into three sub-projects: JUnit Platform, JUnit Jupiter (the modern programming model and extension API), and JUnit Vintage (backward compatibility for JUnit 3 and 4). With parameterized tests, developers can run identical test logic against hundreds of boundary conditions without duplicating code.”
- AAA Pattern Arrange, Act, Assert: Every test case sets up preconditions (Arrange), invokes the target method under test (Act), and verifies expected outcomes (Assert).
- Isolation Principle Zero Order Dependency: Tests must never depend on the execution order or shared mutable state of previous test cases.
💡 Real-World Analogy: The Crash-Test Dummy vs Real Passengers
You do not test automotive brake safety with live passengers on the highway.
Instead, you mount a calibrated Crash-Test Dummy into an isolated simulator, simulate exact collision speeds, and verify that the airbag deployed within 20 milliseconds. That is what JUnit 5 + Mockito does for backend business logic.
Instead, you mount a calibrated Crash-Test Dummy into an isolated simulator, simulate exact collision speeds, and verify that the airbag deployed within 20 milliseconds. That is what JUnit 5 + Mockito does for backend business logic.
⚡ Pro-Tip / Interview Memory Hook: assertThrows & Mockito verify
Testing Exceptions: Never wrap test code in a manual
Verification:
try-catch! Use assertThrows(InvalidCredentialsException.class, () -> authService.login(null, null));.
Verification:
verify(emailService, times(1)).sendWelcomeEmail(any()); proves the method was called with zero side effects on real mail servers.
📂 Module 3_ Unit Testing with Junit 5/.../EmployeeServiceTest01.java & ParameterizedTests.java
package com.accenture.ltt.junit5;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*;
public class EnterpriseTestingSuite {
// 1. Parameterized Testing: Clean multi-input execution
@ParameterizedTest
@ValueSource(strings = {"radar", "level", "kayak", "rotator"})
void shouldIdentifyPalindromes(String word) {
String reversed = new StringBuilder(word).reverse().toString();
assertEquals(word, reversed);
}
// 2. CSV Data-Driven Testing
@ParameterizedTest
@CsvSource({
"100.0, 0.10, 110.0",
"250.0, 0.20, 300.0",
"50.0, 0.00, 50.0"
})
void shouldCalculateTaxCorrectly(double base, double rate, double expected) {
double result = base + (base * rate);
assertEquals(expected, result, 0.001);
}
// 3. Exception Verification
@Test
void shouldThrowWhenInputIsNegative() {
assertThrows(IllegalArgumentException.class, () -> {
if (-5 < 0) throw new IllegalArgumentException("Invalid balance");
});
}
}
👁 Execution & Memory Breakdown • Test Life Cycle
Step 1: JUnit 5 creates a brand-new test class instance per test method execution to enforce absolute state isolation between runs.
Step 2:
@CsvSource reads comma-separated arguments and casts them directly to method parameter types via internal converters.