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);
    }
}