When building secure applications, it’s essential to ensure your Spring Security configuration behaves exactly as expected. Whether it’s custom login, role-based access, or JWT filters — testing these configurations is critical.
In this post, you’ll learn how to perform Testing for Spring Boot Security Configuration using JUnit 5, MockMvc, and @WithMockUser
. We’ll walk through authentication, authorization, and common testing scenarios using com.kscodes.springboot
package structure.

⚙️ Dependencies
Here’s what you need in your pom.xml
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<dependencies> <!-- Web and Security --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Testing --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> |
🛡️ Spring Security Configuration
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
package com.kscodes.springboot.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.web.SecurityFilterChain; @Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/admin/**").hasRole("ADMIN") .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN") .anyRequest().authenticated() ) .formLogin() .and() .httpBasic(); return http.build(); } } |
📁 Sample Controller for Testing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
package com.kscodes.springboot.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class SampleController { @GetMapping("/admin/data") public String adminData() { return "Admin content"; } @GetMapping("/user/data") public String userData() { return "User content"; } @GetMapping("/public") public String publicData() { return "Public content"; } } |
🧪 Security Configuration Test: Authentication & Authorization
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
package com.kscodes.springboot; import com.kscodes.springboot.controller.SampleController; import com.kscodes.springboot.config.SecurityConfig; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.context.annotation.Import; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @WebMvcTest(controllers = SampleController.class) @Import(SecurityConfig.class) public class SecurityConfigurationTest { @Autowired private MockMvc mockMvc; @Test void accessWithoutAuthentication_shouldFail() throws Exception { mockMvc.perform(get("/admin/data")) .andExpect(status().isUnauthorized()); } @Test @WithMockUser(username = "admin", roles = {"ADMIN"}) void adminAccess_shouldSucceed() throws Exception { mockMvc.perform(get("/admin/data")) .andExpect(status().isOk()) .andExpect(content().string("Admin content")); } @Test @WithMockUser(username = "user", roles = {"USER"}) void userAccess_toAdmin_shouldFail() throws Exception { mockMvc.perform(get("/admin/data")) .andExpect(status().isForbidden()); } @Test @WithMockUser(username = "user", roles = {"USER"}) void userAccess_toUser_shouldSucceed() throws Exception { mockMvc.perform(get("/user/data")) .andExpect(status().isOk()) .andExpect(content().string("User content")); } } |
🔄 Testing Login Scenarios
To test form login or basic authentication, you can simulate HTTP POST requests:
1 2 3 4 5 6 7 8 9 |
@Test void testBasicAuth() throws Exception { mockMvc.perform(get("/user/data") .with(httpBasic("user", "password"))) .andExpect(status().isUnauthorized()); // If no real user config } |
You’ll need an actual
UserDetailsService
for real login testing.
💡 Common Security Test Annotations
Annotation | Description |
---|---|
@WithMockUser | Simulates a logged-in user |
httpBasic() | Mocks HTTP Basic Auth |
formLogin() | Mocks form-based login |
@WithUserDetails | Loads actual user details from DB (integration-style test) |
✅ Best Practices for Spring Boot Security Configuration Testing
- Always isolate controller testing with
@WebMvcTest
when verifying access rules. - Use
@WithMockUser
to simulate authenticated users in unit tests. - For integration-level security testing (real login), use
@SpringBootTest
. - Avoid exposing sensitive URLs in test logs or failing outputs.
- If using JWT or OAuth2, consider
SecurityMockMvcRequestPostProcessors.jwt()
.
📚 Summary
In this guide, we explored Testing for Spring Boot Security Configuration using:
MockMvc
for request simulations@WebMvcTest
and@WithMockUser
for unit-level security tests- HTTP status verification for role-based access
- Best practices for writing effective, reliable tests
🔐 Spring Security Official Documentation
- https://docs.spring.io/spring-security/reference/index.html Official docs for understanding Spring Security architecture, filters, roles, and test support.
🧪 Spring Security Testing Module
- https://docs.spring.io/spring-security/reference/testing/index.html Guide to
@WithMockUser
,SecurityMockMvcRequestPostProcessors
, andTestSecurityContextHolder
.