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 (tokenJsonToken.START_OBJECT
); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of typejava.util.ArrayList<model.Profile>
from Object value (tokenJsonToken.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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With