A security configuration that compiles is not necessarily a security configuration that works.
Your tests should prove that:
▪️ Anonymous users are rejected
▪️ Authenticated users can access permitted resources
▪️ Users with the wrong role receive 403 Forbidden
▪️ CSRF protection applies to write operations
▪️ Controllers receive the expected authenticated principal
📗 Study guide for Spring: https://bit.ly/springtify
🔸 TLDR
Spring Security provides test utilities for injecting mock users and custom authentication contexts into integration tests.
This lets you verify your authorization rules through the real Spring Security filter chain—without logging in before every test.

🔸 DEFINITION
A security integration test verifies how authentication and authorization rules interact with your Spring application.
Unlike a controller unit test, it exercises components such as SecurityFilterChain, method security, CSRF protection and MockMvc.
Add the spring-security-test dependency and configure your test with:
@SpringBootTest @AutoConfigureMockMvc class SecurityIntegrationTest { @Autowired MockMvc mockMvc; }
🔸 1️⃣ TEST WITH A MOCK USER
@Test
@WithMockUser(
username = "alice",
roles = "USER"
)
void userCanReadProfile() throws Exception {
mockMvc.perform(get("/api/profile"))
.andExpect(status().isOk());
}@WithMockUser creates an authenticated user inside the test’s SecurityContext. The user does not need to exist in your database. Remember that roles = "USER" creates the authority ROLE_USER.
🔸 2️⃣ USE A MOCK SECURITY CONTEXT
@Test void adminContextCanAccessEndpoint() throws Exception { var authentication = UsernamePasswordAuthenticationToken.authenticated( "alice", null, List.of(new SimpleGrantedAuthority("ROLE_ADMIN")) ); TestSecurityContextHolder .setAuthentication(authentication); mockMvc.perform( get("/api/admin") .with(testSecurityContext()) ).andExpect(status().isOk()); }
A manually created context is useful when your application needs a specific Authentication, custom authorities or a domain-specific principal that @WithMockUser cannot represent.
🔸 3️⃣ VERIFY 401 AND 403 RESPONSES
@Test void anonymousUserIsRejected() throws Exception { mockMvc.perform(get("/api/admin")) .andExpect(status().isUnauthorized()); } @Test @WithMockUser(roles = "USER") void regularUserIsForbidden() throws Exception { mockMvc.perform(get("/api/admin")) .andExpect(status().isForbidden()); }
These tests protect two different rules: 401 means authentication is required, while 403 means the user is authenticated but lacks permission. The exact unauthenticated response depends on your configured entry point.
🔸 4️⃣ TEST A SECURED CONTROLLER WRITE
@Test @WithMockUser(roles = "ADMIN") void adminCanCreateUser() throws Exception { mockMvc.perform(post("/api/users") .with(csrf()) .contentType(APPLICATION_JSON) .content(""" {"name":"Ada"} """)) .andExpect(status().isCreated()) .andExpect(jsonPath("$.name") .value("Ada")); }
For POST, PUT, PATCH and DELETE requests, include .with(csrf()) when CSRF protection is enabled. This verifies the controller, authorization rule, request mapping and response together.
🔸 TAKEAWAYS
▪️ Test denied paths as carefully as permitted paths
▪️ Use @WithMockUser for common role-based scenarios
▪️ Build a custom context for specialized principals
▪️ Distinguish unauthenticated 401 from unauthorized 403
▪️ Include CSRF tokens in secured write-operation tests
▪️ Mock-user tests verify authorization—not your real login process
Security should not be something we assume from the configuration.
It should be executable behavior that our test suite proves. 🔐
#Java #SpringBoot #SpringSecurity #SoftwareTesting #IntegrationTesting #JUnit #MockMvc #CyberSecurity #SoftwareEngineering #BackendDevelopment
Go further with Java certification:
Java👇
Spring👇
SpringBook👇
JavaFullstackBook👇