Testing Spring Security: @WithMockUser and SecurityMockMvc

Learn how to test Spring Security: use @WithMockUser for authentication simulation and SecurityMockMvcRequestBuilders for endpoint security validation with MockMvc.

published: reading time: 18 min read author: GeekWorkBench
Quick Summary

Learn how to test Spring Security: use @WithMockUser for authentication simulation and SecurityMockMvcRequestBuilders for endpoint security validation with MockMvc. The guide uses practical examples to explain introduction to spring security testing, when to use security test annotations and shows how to apply the ideas in a Spring Boot project. It closes with common pitfalls and production checks so you can apply the pattern with fewer surprises.

Testing Spring Security: @WithMockUser, SecurityMockMvcRequestBuilders

Testing secured endpoints in Spring trips up almost every team I have worked with. The problem is not that the tools are complicated, but that security testing has its own set of gotchas that do not play by the same rules as regular unit tests. A test can pass and still be wrong.

This guide walks through the testing toolkit Spring Security provides and, more importantly, when each piece fits and when it will mislead you.

Introduction to Spring Security Testing

When you need to test an endpoint that requires a logged-in user, you have two jobs: simulate authentication and verify that authorization rules actually block what they should. Spring Security’s test support handles both without making you wire up a full login flow.

There are two main paths. Annotation-based testing with @WithMockUser and @WithUserDetails injects a fake authentication into the security context before your test runs. The second path is SecurityMockMvcRequestBuilders, which gives you fine-grained control over building requests with security-specific options for credentials, CSRF tokens, and headers.

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;

@Test
@WithMockUser(roles = "ADMIN")
public void adminEndpointWhenAdminRoleThenOk() throws Exception {
    this.mvc.perform(get("/admin/dashboard"))
        .andExpect(status().isOk())
        .andExpect(authenticated().withUsername("user"));
}

What I like about this approach is that the security filter chain still executes. It is not mocked away entirely. Your tests catch real configuration problems, not just the happy path where everything is wired up correctly.

When to Use Security Test Annotations

These annotations are the right tool when you want to test authorization logic by itself, separate from how users actually authenticate.

Use @WithMockUser for a quick way to simulate any user with any roles. It creates a populated SecurityContext without needing a real user in your system. I reach for this one most often.

@Test
@WithMockUser(username = "testuser", roles = {"USER", "ADMIN"})
public void multiRoleAccessWhenHasAdminRoleThenOk() throws Exception {
    mvc.perform(get("/admin/config"))
        .andExpect(status().isOk());
}

Use @WithUserDetails when your code calls getPrincipal() and expects your custom UserDetails implementation, not Spring Security’s generic User. This annotation looks up an actual user from whatever UserDetailsService you have configured.

@Test
@WithUserDetails(value = "john.doe", userDetailsServiceBeanName = "customUserDetailsService")
public void customPrincipalWhenLookupThenReturnsCustomObject() throws Exception {
    mvc.perform(get("/profile"))
        .andExpect(status().isOk())
        .andExpect(authenticated().withAuthentication(
            authentication -> authentication.getPrincipal() instanceof CustomUserDetails
        ));
}

Use manual SecurityContext manipulation when you need something the annotations cannot express. Expired tokens, custom principal types, partially populated contexts. The annotation approach is clean but limited.

@Test
public void expiredTokenWhenProcessedThenRejectsRequest() throws Exception {
    SecurityContextHolder.getContext().setAuthentication(
        new UsernamePasswordAuthenticationToken(
            "expired-user", null,
            AuthorityUtils.createAuthorityList("ROLE_USER")
        )
    );

    mvc.perform(get("/secure/resource"))
        .andExpect(status().isForbidden());
}

Skip security annotations entirely when you are testing the login flow itself. If you want to verify that valid credentials actually authenticate and invalid ones fail, send real HTTP requests with actual form data.

Do not use @WithMockUser when your code depends on UserDetails methods that Spring Security’s default User does not implement properly. You will get NullPointerException in production that never appeared in tests.

Spring Security Test Filter Chain

When you configure MockMvc with Spring Security, the filter chain runs exactly as it would in production. The only difference is that requests do not go over the network. Every filter still executes.

graph TD
    A[HttpServletRequest] --> B[SecurityFilterChain]
    B --> C[WebAsyncManagerIntegrationFilter]
    B --> D[SecurityContextPersistenceFilter]
    B --> E[HeaderWriterFilter]
    B --> F[CsrfFilter]
    B --> G[LogoutFilter]
    B --> H[UsernamePasswordAuthenticationFilter]
    B --> I[RequestCacheAwareFilter]
    B --> J[SecurityContextHolderAwareRequestFilter]
    B --> K[AnonymousAuthenticationFilter]
    B --> L[SessionManagementFilter]
    B --> M[ExceptionTranslationFilter]
    B --> N[FilterSecurityInterceptor]
    N --> O[AuthorizationManager]
    O --> P{Access Denied?}
    P -->|Yes| Q[AccessDeniedHandler]
    P -->|No| R[Authorized]
    Q --> S[HttpServletResponse]
    R --> S

FilterSecurityInterceptor sits at the end of the chain and asks an AuthorizationManager whether to allow the request. When you use @WithMockUser, the security context is already populated before the chain starts, which means the authentication filters do nothing. That is the point.

What trips people up is this. If you manually set a security context without using @WithMockUser, and you do not clear it between tests, SecurityContextPersistenceFilter can carry that context into subsequent tests running in the same thread. That causes intermittent failures that disappear when you run tests individually.

Failure Scenarios

Even with Spring Security’s test support, several things can catch you off guard.

CSRF Handling

CSRF protection is on by default. Every POST, PUT, DELETE, or PATCH request needs a valid token or Spring Security returns 403.

@Test
public void postEndpointWithoutCsrfThenForbidden() throws Exception {
    mvc.perform(post("/api/data"))
        .andExpect(status().isForbidden());
}

@Test
@WithMockUser
public void postEndpointWithCsrfThenOk() throws Exception {
    mvc.perform(post("/api/data")
            .with(csrf()))
        .andExpect(status().isOk());
}

The .with(csrf()) post-processor pulls the expected token from CsrfTokenRepository and adds it to your request. Without it, even authenticated requests get rejected.

Method Security Not Enforced

This one is insidious. You add @PreAuthorize to a method, write a test that should fail, run it, and it passes. You move on. Then in production, the method is not protected at all.

@PreAuthorize("hasRole('ADMIN')")
public String getAdminData() {
    return "secret information";
}

@Test
@WithMockUser(roles = "USER")
public void methodSecurityWhenUserAccessesAdminMethodThenShouldFail() throws Exception {
    // This passes when it should not if @EnableMethodSecurity is missing
}

The fix is one annotation:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity  // This one
public class SecurityConfig {
    // ...
}

Without @EnableMethodSecurity, Spring Security ignores every method-level annotation. Your tests lie to you.

Authentication vs Authorization Confusion

Authentication answers “who are you?” Authorization answers “what can you do?” These are different questions and you need to test both.

// Authentication test - does the user exist?
@Test
@WithMockUser(username = "validuser")
public void authenticationTest() throws Exception {
    mvc.perform(get("/profile"))
        .andExpect(authenticated().withUsername("validuser"));
}

// Authorization test - does the role grant access?
@Test
@WithMockUser(roles = "ADMIN")
public void authorizationTest() throws Exception {
    mvc.perform(delete("/users/1"))
        .andExpect(status().isForbidden());  // USER role cannot delete
}

I have seen tests that only verify authentication and call it a day. That leaves authorization completely untested.

Comparing Security Test Approaches

Approach Setup Effort Flexibility Realistic Behavior Best For
@WithMockUser Minimal Limited to roles/authorities No real authentication Most authorization tests
@WithUserDetails Moderate Full UserDetails control Real UserDetailsService lookup Custom principal objects
Custom SecurityContext High Complete control Manual context setup Edge cases
SecurityMockMvcRequestBuilders Moderate Request-level control Full filter chain Integration tests
Real HTTP with credentials Moderate Full auth flow Complete real-world End-to-end auth tests

For day-to-day authorization tests, @WithMockUser handles 90% of what you need. @WithUserDetails is for when your code actually calls methods on the UserDetails object. Custom context manipulation is rare.

Implementation Reference

Using @WithMockUser

import org.springframework.security.test.context.support.WithMockUser;

// Default: username "user", password "password", role ROLE_USER
@Test
@WithMockUser
public void basicMockUserTest() throws Exception {
    mvc.perform(get("/user/profile"))
        .andExpect(status().isOk());
}

// Custom username and roles
@Test
@WithMockUser(username = "admin", roles = {"ADMIN", "USER"})
public void customRolesTest() throws Exception {
    mvc.perform(get("/admin/dashboard"))
        .andExpect(status().isOk());
}

// Custom authorities (no ROLE_ prefix)
@Test
@WithMockUser(authorities = "PRIVILEGE_READ")
public void customAuthorityTest() throws Exception {
    mvc.perform(get("/data"))
        .andExpect(status().isOk());
}

Using @WithUserDetails

import org.springframework.security.test.context.support.WithUserDetails;

@Test
@WithUserDetails(value = "customUser", userDetailsServiceBeanName = "myUserDetailsService")
public void customUserDetailsTest() throws Exception {
    mvc.perform(get("/custom/profile"))
        .andExpect(status().isOk())
        .andExpect(authenticated().withUsername("customUser"));
}

Using SecurityMockMvcRequestBuilders

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;

@Test
public void formLoginTest() throws Exception {
    mvc.perform(formLogin("/login")
            .user("validuser")
            .password("validpassword"))
        .andExpect(status().isFound());  // 302 redirect after login
}

@Test
public void invalidLoginTest() throws Exception {
    mvc.perform(formLogin("/login")
            .user("invalid")
            .password("wrong"))
        .andExpect(status().isUnauthorized());
}

Authorization Matchers

import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;

@Test
@WithMockUser(roles = "ADMIN")
public void authorizedAccessTest() throws Exception {
    mvc.perform(get("/admin/settings")
            .with(csrf()))
        .andExpect(status().isOk())
        .andExpect(authenticated().withUsername("user")
            .withRoles("ADMIN"));
}

@Test
public void unauthorizedAccessTest() throws Exception {
    mvc.perform(get("/admin/secret"))
        .andExpect(status().isUnauthorized());
}

@Test
@WithMockUser
public void forbiddenAccessTest() throws Exception {
    mvc.perform(get("/admin/restricted"))
        .andExpect(status().isForbidden());
}

CSRF Configuration in Tests

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;

// State-changing requests need CSRF
@Test
@WithMockUser
public void postWithCsrf() throws Exception {
    mvc.perform(post("/api/item")
            .with(csrf()))
        .andExpect(status().isCreated());
}

// Disable CSRF for specific public endpoints
@Test
public void publicEndpointWithoutCsrf() throws Exception {
    mvc.perform(post("/public/webhook")
            .with(csrf().disable()))
        .andExpect(status().isOk());
}

Using hasRole() and permitAll()

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;

@Test
public void permitAllEndpointAccessibleWithoutAuth() throws Exception {
    mvc.perform(get("/public/info"))
        .andExpect(status().isOk());
}

@Test
public void securedEndpointRequiresAuth() throws Exception {
    mvc.perform(get("/secure/data"))
        .andExpect(status().isUnauthorized());
}

@Test
@WithMockUser(roles = "USER")
public void roleBasedAccess() throws Exception {
    mvc.perform(get("/user/dashboard"))
        .andExpect(status().isOk());
}

Setting Up SecurityMockMvc

The setup matters. Get this wrong and everything silently fails.

import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = SecurityConfig.class)
@WebAppConfiguration
public class SecurityTestBase {

    @Autowired
    private WebApplicationContext context;

    protected MockMvc mvc;

    @BeforeEach
    public void setup() {
        mvc = MockMvcBuilders
            .webAppContextSetup(context)
            .apply(springSecurity())
            .build();
    }
}

.apply(springSecurity()) adds the security filter chain to MockMvc. Without it, @WithMockUser does nothing. I have wasted more time than I would like to admit on that one.

Observability Checklist

When you write security tests, check that you are covering:

  • Authentication succeeds with valid credentials and returns correct status codes
  • Unauthenticated requests return 401
  • Authenticated but unauthorized requests return 403
  • CSRF protection blocks requests without valid tokens (403)
  • Session management behaves correctly for authenticated users
  • Security headers appear in responses when configured
  • Method-level annotations are enforced (with @EnableMethodSecurity present)
  • Logout clears authentication and invalidates the session
  • Concurrent session limits work when session management is configured
  • Remember-me works when enabled

Security Notes for Tests

A few things to keep in mind when writing these tests.

Test Credentials

Do not use real production credentials in test code. Ever. Use test-only values and keep them isolated from anything that touches production.

// Bad: real password in test code
@Test
public void badPractice() throws Exception {
    mvc.perform(post("/login")
            .param("username", "admin@company.com")
            .param("password", "SuperSecret123!"));
}

// Good: test-only credentials
@Test
public void goodPractice() throws Exception {
    mvc.perform(post("/login")
            .param("username", "testuser@example.com")
            .param("password", "testPassword123!"));
}

Secure Test Configurations

Keep test security configs separate from production configs. Disable CSRF in tests if needed for public endpoints, but make sure it is only active in test profiles.

@TestConfiguration
public class TestSecurityConfig {

    @Bean
    @Profile("test")
    public SecurityFilterChain testSecurityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())  // Test profile only
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/test/**").permitAll()
                .anyRequest().authenticated()
            );
        return http.build();
    }
}

Common Pitfalls

Method Security Not Triggering

Forgetting @EnableMethodSecurity is the most common reason method security does not work. The annotation processor does nothing without it.

@EnableMethodSecurity  // Required for @PreAuthorize, @Secured, @RolesAllowed
@Configuration
public class SecurityConfig {
    // ...
}

Password Encoding Mismatches

When using @WithUserDetails, the password encoder in your test UserDetailsService must match your production encoder. A mismatch silently breaks authentication.

@TestConfiguration
public class TestUserDetailsConfig {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Bean
    public UserDetailsService testUserDetailsService() {
        UserDetails user = User.builder()
            .username("testuser")
            .password(passwordEncoder.encode("testpass"))  // Same encoder as prod
            .roles("USER")
            .build();

        return new InMemoryUserDetailsManager(user);
    }
}

Context Propagation Between Tests

SecurityContextHolder uses thread-local storage. If one test sets a context manually and does not clean up, following tests in the same thread inherit it.

@AfterEach
public void cleanupSecurityContext() {
    SecurityContextHolder.clearContext();
}

@WithMockUser cleans up automatically. Manual context manipulation does not.

Testing Actuator Endpoints

Actuator endpoints have their own security rules. If your test config locks down everything, you may find that actuator/health returns 403 when you expected 200.

@Test
public void actuatorHealthWhenAnonymousThenOk() throws Exception {
    mvc.perform(get("/actuator/health"))
        .andExpect(status().isOk());
}

When NOT to Use

These annotations have real limits. There are situations where they will actively mislead you if you stick with them.

Testing Login Flows and Credential Verification

@WithMockUser and @WithUserDetails skip the actual authentication mechanism. They drop a pre-built Authentication object straight into the SecurityContext, bypassing the AuthenticationProvider chain completely.

When you need to check whether:

  • A valid username and password combination actually creates a session
  • Account lockout triggers after failed attempts
  • Password migration or encoding detection works
  • Multi-factor authentication flow completes end-to-end

reach for @SpringBootTest with webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT and send real HTTP requests with actual form data or HTTP Basic credentials.

Testing Against a Real User Store

@WithUserDetails calls your UserDetailsService, which works fine for integration testing. But if your user store is a database with connection pooling, an LDAP server, or an external identity provider, test isolation suffers and test runs slow down.

For true end-to-end tests against a real user store, look at:

  • Testcontainers to spin up a database or LDAP container
  • A separate test profile pointing to a sandboxed user store
  • Spring Boot’s @TestConfiguration to swap beans with test doubles

Security Tests for Public Endpoints

If an endpoint is permitAll, @WithMockUser tests are pointless noise. Spring Security does nothing for these paths, so you end up testing that the framework is configured not to intercept them.

Write standard controller tests instead. Verify the endpoint works with anonymous access, no security annotations needed.

Overly Complex Security Test Setups

When the test setup starts exceeding the complexity of the code it protects, you have a problem. Watch out for these warning signs:

  • Elaborate custom SecurityContext hierarchies
  • Dozens of mock users with fine-grained role combinations
  • Custom RequestPostProcessor chains that rival the application code in complexity

Step back and ask whether a simpler test strategy or a higher-level integration test would give you better confidence with less maintenance overhead.

Verifying Actual HTTP Behavior

Security test annotations operate at the MockMvc level. They miss issues that only surface in real HTTP conversations:

  • Session cookie handling and domain scoping
  • CORS preflight request behavior
  • HTTP header ordering and case sensitivity
  • Timing attacks on authentication endpoints

For these, use Selenium or another browser automation tool with a real Spring Boot test server running.

Trade-Off Table

No single approach wins on all fronts. Here is how the main options stack up.

Approach Setup Effort Flexibility Realistic Behavior Best Use Cases
@WithMockUser Low Medium Low (fake user, no backend lookup) Role-based authorization, method security, quick feedback
@WithUserDetails Medium Medium Medium (real UserDetailsService called) Custom UserDetails behavior, integration with user stores
Custom SecurityContext manipulation Medium High Low to Medium (manually crafted auth) Edge cases, negative tests, complex authentication states
SecurityMockMvcRequestBuilders Medium High Medium (HTTP-level simulation) Form login, OAuth2 client tests, custom filter behavior
Real HTTP testing (@SpringBootTest) High Medium High (full stack, real authentication) Login flows, session management, end-to-end security verification

Detailed Trade-Offs

@WithMockUser gives you a user without the overhead of a real UserDetailsService. It builds a User object on the spot with hardcoded fields, so it cannot test anything that depends on how your user store constructs the principal or how your AuthenticationProvider validates credentials. The payoff is that you can add one annotation and have an authenticated user immediately.

@WithUserDetails pulls the user from your actual UserDetailsService. This means your tests catch problems in user loading, attribute mapping, and authority translation, but you pay for it in test isolation and speed.

Custom SecurityContext manipulation is the middle ground. You decide exactly what goes into the Authentication object, which helps when you need to test an edge case that @WithMockUser cannot express. The cost is manual cleanup and no guarantee that the real authentication flow would produce the same result.

SecurityMockMvcRequestBuilders (post/get with user(…)) push the request through the actual filter chain rather than injecting a pre-built Authentication. This makes them more realistic for form login and OAuth2 client credentials tests, but they require more setup and run slower than the annotation approach.

Real HTTP testing is the most realistic option. The full filter chain fires, sessions are created, cookies are exchanged, and CORS preflight requests are handled properly. You can even plug in Selenium for browser-level testing. The downsides are slow test runs, infrastructure requirements, and occasional flakiness from timing issues.

Decision Guide

Need fast feedback on authorization rules? Start with @WithMockUser. Testing how your code uses UserDetails fields? Use @WithUserDetails. Testing authentication flows like login, logout, or OAuth2? Reach for SecurityMockMvcRequestBuilders or real HTTP tests. Session or cookie behavior requires real HTTP tests with @SpringBootTest. Account lockout, password expiry, and brute-force protection? Use @SpringBootTest with a test container.

Quick Recap Checklist

When writing Spring Security tests:

  • Configure MockMvc with springSecurity() before running tests
  • Add @EnableMethodSecurity if using @PreAuthorize or @Secured
  • Include .with(csrf()) on POST, PUT, DELETE requests
  • Use @WithMockUser for most role-based authorization tests
  • Use @WithUserDetails when testing custom UserDetails behavior
  • Clear SecurityContext after manual context manipulation
  • Verify 401 for unauthenticated, 403 for unauthorized, 200 for authorized
  • Test both positive and negative authorization cases
  • Match password encoder in tests to production
  • Verify security headers are present when configured
  • Clean up security context between tests to avoid leakage

Interview Questions

1. How does @WithMockUser differ from @WithUserDetails in Spring Security testing?

@WithMockUser creates a fake authentication object directly in the SecurityContext without checking whether the user exists anywhere. It generates a User with the specified username, password, and roles on the spot. @WithUserDetails does not mock anything; it calls your actual UserDetailsService to load the user from your configured user store.

Use @WithMockUser when you just need a user with certain roles to test authorization rules. Use @WithUserDetails when your code calls methods on the UserDetails object that Spring Security's generic User does not implement, or when you want to test the integration between your user store and security configuration.

2. Why might a test with @WithMockUser pass even though @PreAuthorize is not working?

The test passes because @WithMockUser populates the SecurityContext successfully, but @PreAuthorize never runs to check the authorization rule. This happens when @EnableMethodSecurity is missing from your configuration class.

Spring Security has two separate authorization systems. The filter chain (which handles HTTP request authorization) runs by default. Method-level security (which handles @PreAuthorize, @Secured, @RolesAllowed) requires @EnableMethodSecurity to activate. Without it, the annotations are parsed but ignored, so your tests never actually verify the method-level rules.

3. What does the csrf() request post-processor do in Spring Security MockMvc tests?

csrf() adds a valid CSRF token to the request. Spring Security enables CSRF protection by default, which means every state-changing request (POST, PUT, DELETE, PATCH) must carry a matching token. The CsrfFilter rejects any request that does not have one with a 403 response.

The post-processor retrieves the expected token from CsrfTokenRepository and attaches it to your request, either as a request parameter or header depending on your CSRF configuration. For endpoints where CSRF is not relevant, you can disable it with csrf().disable().

4. How do you test that an endpoint returns 401 for unauthenticated users and 403 for authenticated but unauthorized users?

For 401, make the request without any authentication. Spring Security's ExceptionTranslationFilter catches the fact that no authentication is present and returns 401:

mvc.perform(get("/secure/resource")).andExpect(status().isUnauthorized());

For 403, authenticate with a user that does not have the required role:

@Test @WithMockUser(roles = "USER") public void thenForbidden() throws Exception { mvc.perform(get("/admin/dashboard")).andExpect(status().isForbidden()); }

The distinction matters: 401 means no credentials were provided at all. 403 means credentials were provided but the authenticated principal lacks permission to access the resource.

5. What is the purpose of SecurityMockMvcConfigurers.springSecurity() in test setup?

springSecurity() is a MockMvc configurer that inserts Spring Security's filter chain into the test request processing pipeline. It does four things: adds the security filters to MockMvc, makes the security context persist across sequential requests in the same test, aligns SecurityContextHolder with the test thread, and enables the security test annotations like @WithMockUser to work.

Without calling apply(springSecurity()), the security filters are not in the chain. The annotations do nothing. The test compiles and runs but tests nothing related to security.

Further Reading

For more Spring Security concepts and configuration patterns, explore the Spring Boot Learning Path which covers security fundamentals alongside dependency injection, data access, and deployment strategies.

Conclusion

Spring Security’s test support gives you multiple ways to verify your application handles authentication and authorization correctly. For most authorization tests, @WithMockUser is the right starting point. When your code depends on UserDetails, reach for @WithUserDetails. For edge cases and complex authentication states, manual SecurityContext manipulation gives you complete control.

The most common mistakes are forgetting @EnableMethodSecurity when using @PreAuthorize, omitting .with(csrf()) on state-changing requests, and letting SecurityContext leak between tests. The observability checklist and common pitfalls section cover these in detail.

OAuth2 resource server testing follows the same principles but uses JWT validation instead of session-based authentication. Cover token presence, validity, expiry, and scope grants in your tests.

Category

Related Posts

Spring Boot Build Tools: Maven & Gradle

Configure Maven and Gradle for Spring Boot projects—plugins, dependency management, packaging JARs and WARs, and build automation essentials.

#spring-boot #spring-boot-roadmap #learning-path

Embedded Web Servers in Spring Boot: Tomcat, Jetty, Undertow

Configure embedded servers in Spring Boot: compare Tomcat, Jetty, and Undertow, tune thread pools, enable access logs, and switch implementations.

#spring-boot #spring-boot-roadmap #learning-path

JUnit 5 & Jupiter: Lifecycle, Nested & Parameterized Tests

Explore JUnit 5 Jupiter features: master test lifecycle annotations, organize tests with @Nested, and parameterize tests with @CsvSource and @MethodSource.

#spring-boot #spring-boot-roadmap #learning-path