I am testing/using the new RestTestClient with Spring Boot 4 and I wanted to test a controller method if a role is not matching. But instead of status code 403 (or 401 if no credentials are given) the returning status code is 500 (Internal Server Error).
I figured out that org.springframework.test.web.servlet.client.MockMvcClientHttpRequestFactory.MockMvcClientHttpRequest on line 100/101 is the cause for the code 500. (Version Spring Framework 7.0.1)
Is this an intended behavior or does it need to be improved?
What I expect
Even with SpringBootTest.WebEnvironment.MOCK I want the same http status code on security exceptions like on a real http request.
Here is some example code:
Controller
@RestController
@RequestMapping("/api")
public class OneController {
// Instead of 401 or 403 the @PreAuthorize returns 500 to the test result if not matching
@GetMapping("/one")
@PreAuthorize( "hasRole('ADMIN')")
public String getOne() {
return "One";
}
// This is returning StatusCode 402 to the test expectation
@GetMapping("/two")
public String getTwo() {
throw new ResponseStatusException(HttpStatus.PAYMENT_REQUIRED);
}
}
Test-Class
@SpringBootTest
@AutoConfigureRestTestClient
public class OneControllerTest {
@Test
void testWrongRole(@Autowired RestTestClient client) {
Authentication authentication = new AnonymousAuthenticationToken(
"key", "admin", List.of(new SimpleGrantedAuthority("USER")) // ADMIN is expected
);
SecurityContextHolder.getContext().setAuthentication(authentication);
// With none matching @PreAuthorize on the controller method,
// the test will fail with status 500 instead of expected code 403
client.get().uri("/api/one")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(HttpStatus.FORBIDDEN);
}
@Test
void testStatusException(@Autowired RestTestClient client) {
// 402 is matching the thrown exception from the controller method
client.get().uri("/api/two")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isEqualTo(HttpStatus.PAYMENT_REQUIRED);
}
}