Overview
Three distinct test patterns are used in the auth-service, each with different levels of Spring context loading, speed, and isolation.
1. Unit Test — Pure JUnit + Mockito
Examples: JwtWebFilterTest, SecurityConfigTest
@ExtendWith(MockitoExtension.class)
class JwtWebFilterTest {
@Mock
private ServiceClient serviceClient;
@InjectMocks
private CustomUserDetailService customUserDetailService;
}
| Aspect | Detail |
|---|---|
| Spring Context | None — no Spring loaded at all |
| Dependencies | All mocked with @Mock |
| Injection | @InjectMocks (Mockito creates the object, injects mocks) |
| Speed | Fastest (~milliseconds) |
| What it tests | Single class logic in isolation |
| Needs env vars | No |
| Needs services running | No |
2. Component Test — Partial Spring Context
Example: ReactiveAuthenticationManagerTest
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {AuthServiceConfig.class, CustomUserDetailService.class})
class ReactiveAuthenticationManagerTest {
@MockitoBean
private ServiceClient serviceClient;
@Autowired
private ReactiveAuthenticationManager reactiveAuthenticationManager;
}
| Aspect | Detail |
|---|---|
| Spring Context | Partial — only listed classes loaded |
| Dependencies | Mix of real beans (@Autowired) and mocks (@MockitoBean) |
| Injection | Spring DI — real wiring between beans |
| Speed | Fast (~2 seconds) |
| What it tests | Bean wiring and interaction between 2-3 real components |
| Needs env vars | No — JwtUtil, AuthController etc. never created |
| Needs services running | No |
Key Annotations Explained
@ExtendWith(SpringExtension.class)— Tells JUnit 5 to use Spring’s test support. Enables@Autowiredand@MockitoBean. Lightweight alternative to@SpringBootTest.@ContextConfiguration(classes = {...})— Tells Spring exactly which classes to load. Only those beans are created — nothing else.
Why not @SpringBootTest?
@SpringBootTest loads the entire application — all beans, all configs, component scan. This pulls in JwtUtil which needs JWT_SECRET, AuthController which needs AuthService, etc. For a test that only needs ReactiveAuthenticationManager + CustomUserDetailService, loading the full context is wasteful and requires unnecessary env vars.
3. Integration Test — No Spring, Real HTTP
Examples: LoginFlowIT, RegistrationFlowIT, LogoutFlowIT
@Slf4j
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class LoginFlowIT {
private static final RestTemplate restTemplate = new RestTemplate();
}
| Aspect | Detail |
|---|---|
| Spring Context | None in the test — services run externally |
| Dependencies | Real running services (auth, member, PostgreSQL) |
| Injection | None — uses RestTemplate for real HTTP calls |
| Speed | Slowest (depends on network + service response) |
| What it tests | Full end-to-end flow across multiple services |
| Needs env vars | Yes — env.mac for Google credentials |
| Needs services running | Yes — auth-service, member-service, Eureka, etc. |
Key Annotations Explained
@Slf4j— Lombok generates alogfield for logging (log.info(...))@TestInstance(Lifecycle.PER_CLASS)— JUnit creates one test instance for all methods (instead of new instance per method). Lets@BeforeAll/@AfterAllbe non-static, and lets methods share state (e.g., tokens from login used in refresh)@TestMethodOrder(OrderAnnotation.class)— Tests run in@Order(1),@Order(2),@Order(3)sequence. Critical because login must happen before refresh, refresh before revoke
4. Stress Test — Sequential/Concurrent Load
Example: StressTestIT
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class StressTestIT {
private static final RestTemplate restTemplate = new RestTemplate();
private static final int TOTAL_FLOWS = 5;
}
| Aspect | Detail |
|---|---|
| Spring Context | None — services run externally |
| Dependencies | Real running services (auth, member, PostgreSQL) |
| Injection | None — uses RestTemplate for real HTTP calls |
| Speed | Slowest (multiple full flows) |
| What it tests | Repeated full flows (Login → Refresh → Revoke) to verify stability under load |
| Needs env vars | Yes — env.mac for Google credentials |
| Needs services running | Yes — auth-service, member-service, Eureka, etc. |
How it differs from Integration Test
| Integration Test | Stress Test | |
|---|---|---|
| Goal | Verify correctness of a single flow | Verify stability under repeated/concurrent load |
| Repetitions | 1 flow per test method | Multiple flows (5+ sequential, optionally concurrent) |
| Metrics | Pass/fail only | Response times (avg, P50, P90, P99), throughput, success rate |
| Warmup | No | Yes — primes connection pools before measurement |
| Report | JUnit pass/fail | Detailed report with per-step timing saved to file |
| Setup/Cleanup | Per-flow test data | Registers user once in @BeforeAll, deletes in @AfterAll |
Side-by-Side Comparison
| Unit | Component | Integration | Stress | |
|---|---|---|---|---|
| Spring Context | None | Partial (2-3 beans) | None (external) | None (external) |
| Real Beans | 0 | 2-3 | All (running externally) | All (running externally) |
| Mocked | Everything | Service boundaries only | Nothing | Nothing |
| HTTP Calls | No | No | Yes | Yes (repeated) |
| Services Running | No | No | Yes | Yes |
| Speed | ~ms | ~2s | ~seconds | ~10+ seconds |
| Goal | Logic correctness | Bean wiring correctness | E2E flow correctness | Stability + performance under load |
| Key Annotation | @ExtendWith(MockitoExtension) | @ExtendWith(SpringExtension) + @ContextConfiguration | @TestInstance(PER_CLASS) + @TestMethodOrder | @TestMethodOrder + warmup/report phases |
5. Full Spring Context Test (Not Used Yet)
This pattern is not currently used in the project, but is worth documenting for future reference.
What it would look like
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("mock")
class AuthEndpointTest {
@Autowired
private WebTestClient webTestClient;
@MockitoBean
private ServiceClient serviceClient;
@Test
void login_shouldReturnTokens_whenValidGoogleIdToken() {
// Mock member-service response
when(serviceClient.postReactive(...)).thenReturn(Mono.just(mockResponse));
webTestClient.post().uri("/auth/token")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(loginRequest)
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.accessToken").isNotEmpty()
.jsonPath("$.refreshToken").isNotEmpty();
}
}
| Aspect | Detail |
|---|---|
| Spring Context | Full — all beans loaded (AuthController, AuthService, JwtUtil, SecurityConfig, etc.) |
| Server | Embedded Netty on random port |
| Dependencies | Real beans + mocked external boundaries (@MockitoBean on ServiceClient) |
| HTTP Client | WebTestClient (in-process, no real network) |
| Needs env vars | Yes — JWT_SECRET, GOOGLE_CLIENT_ID, token expirations |
| Needs services running | No — member-service mocked via @MockitoBean |
| Speed | ~5-10 seconds (full context startup) |
Where it fits
Unit → Component → Full Context → Integration → Stress
(no Spring) (partial Spring) (full Spring, (real services, (repeated real
mocked boundaries) real HTTP) flows + metrics)
Potential example use cases
- Test the full reactive chain from controller to service: Verify that
AuthController.issueToken()correctly callsAuthService, which callsReactiveAuthenticationManager, which callsCustomUserDetailService— all wired by real Spring DI with onlyServiceClientmocked - Test SecurityWebFilterChain with real endpoints: Verify that open endpoints (
/auth/token,/auth/users) are accessible without JWT, while protected endpoints require ADMIN role - Test error handling across layers: Verify that a
ServiceClientExceptionfrom member-service correctly propagates throughAuthServiceand returns the right HTTP status and error body fromAuthController - Test JWT token generation and validation round-trip: Verify that
JwtUtil.generate()creates a valid token thatJwtWebFiltercan parse and set inReactiveSecurityContextHolder
Why we don’t use it yet
The current test strategy covers the needed scenarios:
- Bean wiring correctness → Component tests handle this
- End-to-end flow → Integration tests handle this with real services
A full context test would be useful when we need to test internal wiring + HTTP layer + error handling together without requiring all services to be running.
