Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing an exception instead of List of entities is not working, when using TestRestTemplate and MockRestServiceServer

I have a simple controller (CODE)

@RestController
@RequestMapping("/profiles" , produces = [MediaType.APPLICATION_JSON_VALUE])
class Controller(@Autowired val restClient: RestClient) {

    @GetMapping("/simple-get")
    fun simpleGetCall(): List<Profile> {
        restClient.callClientGet()
        return listOf(
                    Profile("firstname1", "lastname1"),
                    Profile("firstname2", "lastname2"))
    }
}

the controller is calling the RestClient which is making a client call (CODE)

@Service
class RestClient(@Autowired val restTemplate: RestTemplate) {
    fun callClientGet(){
        try {
            restTemplate.getForEntity(
                "/profiles/simple-get",
                Number::class.java
            )
        }catch(exception: Exception){
            throw MyClientCallException("this is an exception")
        }
    }
}

MyClientCallException looks like this (CODE)

class MyClientCallException(message: String) :
    ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, message)

I have 3 functional tests in my class (CODE) I use the org.springframework.test.web.client.MockRestServiceServer as server

@Test
fun `when calling service, and client returns Accepted, status should be OK`() {

    val responseType: ParameterizedTypeReference<List<Profile>> =
        object : ParameterizedTypeReference<List<Profile>>() {}

    mockServer?.expect(MockRestRequestMatchers.requestTo(URI("http://localhost:8082/profiles/simple-get")))
        ?.andExpect(MockRestRequestMatchers.method(HttpMethod.GET))
        ?.andRespond(
            MockRestResponseCreators.withStatus(HttpStatus.ACCEPTED)
                .contentType(MediaType.APPLICATION_JSON))

    val response = testRestTemplate.exchange(
        "/profiles/simple-get",
        HttpMethod.GET,
        null,
        responseType
    )
    response.statusCode shouldBe HttpStatus.OK
    response.body?.size shouldBe 2
}

This test is working as expected. The client is returning ACCEPTED and the server call is returning the List<Profile>

the second test is failing.

@Test
fun `when calling service, and client timeout, it should return 500 - not working, problems with parsing`() {

    val responseType: ParameterizedTypeReference<List<Profile>> =
        object : ParameterizedTypeReference<List<Profile>>() {}

    mockServer?.expect(MockRestRequestMatchers.requestTo(URI("http://localhost:8082/profiles/simple-get")))
        ?.andExpect(MockRestRequestMatchers.method(HttpMethod.GET))
        ?.andRespond(
            MockRestResponseCreators.withStatus(HttpStatus.REQUEST_TIMEOUT)
                .contentType(MediaType.APPLICATION_JSON))

    val response = testRestTemplate.exchange(
        "/profiles/simple-get",
        HttpMethod.GET,
        null,
        responseType
    )
    response.statusCode shouldBe HttpStatus.INTERNAL_SERVER_ERROR
}

When I do the client call, it's faking a time out exception, something which can always happen. Then the returned Exceptions can't be parsed to the List<Profile> which then give the following error:

org.springframework.web.client.RestClientException: Error while extracting response for type [java.util.List<? extends model.Profile>] and content type [application/json]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type java.util.ArrayList<model.Profile> from Object value (token JsonToken.START_OBJECT); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type java.util.ArrayList<model.Profile> from Object value (token JsonToken.START_OBJECT) at [Source: (PushbackInputStream); line: 1, column: 1]

The next 2 tests are workarounds, but both are not OK for me.

@Test
fun `when calling service, and client timeout, it should return 500 working `() {

    val responseType: ParameterizedTypeReference<List<Profile>> =
        object : ParameterizedTypeReference<List<Profile>>() {}

    mockServer?.expect(MockRestRequestMatchers.requestTo(URI("http://localhost:8082/profiles/simple-get")))
        ?.andExpect(MockRestRequestMatchers.method(HttpMethod.GET))
        ?.andRespond(
            MockRestResponseCreators.withStatus(HttpStatus.REQUEST_TIMEOUT)
                .contentType(MediaType.APPLICATION_JSON))


    assertThrows<RestClientException> {
        testRestTemplate.exchange(
            "/profiles/simple-get",
            HttpMethod.GET,
            null,
            responseType
        )
    }

}

The response type is still as in the original. but it's catching the RestClientException. I was expecting to throw my MyClientCallException and also catch it. (it returns 500, which is good, but I want to see my own exception. This is just an example, maybe I want to return a different error code)

@Test
fun `when calling service, and client timeout, it should return 500 - but response type is wrong`() {

    mockServer?.expect(MockRestRequestMatchers.requestTo(URI("http://localhost:8082/profiles/simple-get")))
        ?.andExpect(MockRestRequestMatchers.method(HttpMethod.GET))
        ?.andRespond(
            MockRestResponseCreators.withStatus(HttpStatus.REQUEST_TIMEOUT)
                .contentType(MediaType.APPLICATION_JSON))

    val response = testRestTemplate.exchange(
        "/profiles/simple-get",
        HttpMethod.GET,
        null,
        String::class.java
    )

    response.statusCode shouldBe HttpStatus.INTERNAL_SERVER_ERROR

}

Second one: I just parse everything as a String, which is helping to solve it. but I'm awaiting originally a List<Profile> so, telling the testRestTemplate, that I want just a String as a response would fake the test.

Is there any way, to make this better or solve this problem?

You can checkout the complete project: https://github.com/joergi/tryouts/tree/main/kotlin-mockrestserver for better tryout.

P.S. I know, this question is similar to Spring TestRestTemplate parse Error instead of HttpClientErrorException thrown but it is still unanswered.

like image 627
Joergi Avatar asked Nov 07 '22 01:11

Joergi


1 Answers

Then the returned Exceptions can't be parsed to the List which then give the following error: - You are asking jackson to map exception to profile list which is obviously failing.

Why not use response type as MyClientCallException instead of ParameterizedTypeReference<List> ? - this should allow jackson to parse the exception response stream to MyClientCallException

Also if you would like to know why RestClientException is not treated same as other client errors - https://github.com/spring-projects/spring-framework/commit/b84fefc430cdf10d428b02f6d61009b7460ae26f#diff-309e077b6d47c54260c59a899971042456c5e03eb5e96e5121c31842b55deedb

like image 179
s7vr Avatar answered Nov 11 '22 05:11

s7vr