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.