Affects: 5.3.1

@Controller
class TestFetchController {

    @Autowired
    lateinit var repository: TestRepository

    @RequestMapping("/categoriesfetch/", method = [RequestMethod.GET])
    @ResponseBody
    suspend fun categoriesGetFetched(
        @RequestParam page: Int? = 0,
        @RequestParam maxResults: Int? = 100
    ): ResponseEntity<*> {

        val response: Page<Test> = repository.findAll(
            PageRequest.of(page!!, maxResults!!)
        )

        val headers = Consumer<HttpHeaders> {
            it.add("TotalElements", response.totalElements.toString())
            it.add("TotalPages", response.totalPages.toString())
            it.add("PerPage", response.numberOfElements.toString())
            it.add("CurrentPage", response.number.toString())
        }

        return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_JSON)
            .headers(headers)
            .body(response)
    }
}

Returns the following response when using MockMvc:

Async:
    Async started = true
     Async result = <200 OK OK,[TestEntity(id=1, modificationDate=2020-11-13 10:04:07.613, modifierId=user, level=0, parentNode=null, displayName=Test0, relations=[]), TestEntity(id=2, modificationDate=2020-11-13 10:04:07.713, modifierId=user, level=0, parentNode=null, displayName=Test1, relations=[]), TestEntity(id=3, modificationDate=2020-11-13 10:04:07.719, modifierId=user, level=0, parentNode=null, displayName=Test2, relations=[]), TestEntity(id=4, modificationDate=2020-11-13 10:04:07.725, modifierId=user, level=0, parentNode=null, displayName=Test3, relations=[]), TestEntity(id=5, modificationDate=2020-11-13 10:04:07.73, modifierId=user, level=0, parentNode=null, displayName=Test4, relations=[])],[Content-Type:"application/json", TotalElements:"30", TotalPages:"6", PerPage:"5", CurrentPage:"0"]>

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

The most interesting part IMO is the [Content-Type:"application/json", TotalElements:"30", TotalPages:"6", PerPage:"5", CurrentPage:"0"] and the missing body.

When removing the suspend keyword everything works as expected. The same applies when returning List directly without the ResponseEntity (without the header part of course):

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

Please note the missing body.

The MockMVC test:

 @Test
    @WithMockUser
    fun `lazy loaded relationships are fetched via fetch`() {

        val maxResults = 5

        mockMvc.get("/categoriesfetch/?page=0&maxResults=$maxResults") {
            header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
        }.andExpect {
            status { isOk() }
            header {
                string(PassHeaders.TOTALPAGES, "6")
                string(PassHeaders.TOTALELEMENTS, "30")
                string(PassHeaders.PERPAGE, "5")
                string(PassHeaders.CURRENTPAGE, "0")
            }
        }.andReturn().response.contentAsString.let { body ->
            val parsedCategories: List<TestEntity> = jacksonObjectMapper().readValue(body)
        }
    }

Please note: the request does work fine when actually running the Spring Boot app and querying the URL. The issue only applies within MockMVC tests.

Comment From: jonasbark

Apparently I have to add .asyncDispatch() to mockMvc.get(...) - sorry!