使用 TestRestTemplate 和 MockRestServiceServer

问题描述

我有一个简单的控制器 (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"))
    }
}

控制器正在调用正在发起客户端调用 (CODE) 的 RestClient

@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 看起来像这样 (CODE)

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

我的班级有 3 个功能测试 (CODE) 我使用 org.springframework.test.web.client.MockRestServiceServer 作为服务器

@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
}

此测试按预期进行。客户端返回 ACCEPTED,服务器调用返回 List<Profile>

第二次测试失败。

@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",responseType
    )
    response.statusCode shouldBe HttpStatus.INTERNAL_SERVER_ERROR
}

当我进行客户端调用时,它会假装超时异常,这种情况总是会发生。 然后返回的 Exceptions 无法解析为 List<Profile>,然后给出以下错误

org.springframework.web.client.RestClientException: 时出错 提取类型 [java.util.List扩展模型。配置文件>] 和内容类型 [application/json];嵌套异常是 org.springframework.http.converter.HttpMessageNotReadableException: JSON 解析错误:无法反序列化类型的值 java.util.ArrayList<model.Profile> 来自对象值(令牌 JsonToken.START_OBJECT);嵌套异常是 com.fasterxml.jackson.databind.exc.MismatchedInputException:不能 反序列化 java.util.ArrayList<model.Profile> 类型的值来自 [来源: (pushbackinputstream);行:1,列:1]

接下来的 2 个测试是变通方法,但对我来说都不好。

JsonToken.START_OBJECT

响应类型仍与原始类型相同。但它捕获了 RestClientException。 我期待抛出我的 MyClientCallException 并抓住它。 (它返回500,这很好,但我想看看自己的异常。这只是一个例子,也许我想返回不同的错误代码

@Test
fun `when calling service,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",responseType
        )
    }

}

第二个:我只是将所有内容解析为一个字符串,这有助于解决它。 但我最初正在等待一个 @Test fun `when calling service,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",String::class.java ) response.statusCode shouldBe HttpStatus.INTERNAL_SERVER_ERROR } 所以,告诉 List<Profile>,我只想要一个字符串作为响应会伪造测试。

有什么办法可以改善或解决这个问题吗?

您可以查看完整的项目:https://github.com/joergi/tryouts/tree/main/kotlin-mockrestserver 以获得更好的试用。

附言我知道,这个问题类似于Spring TestRestTemplate parse Error instead of HttpClientErrorException thrown,但仍然没有答案。

解决方法

然后返回的异常无法解析到列表,然后给出以下错误: - 您要求 jackson 将异常映射到配置文件列表,这显然是失败的。

为什么不使用响应类型作为 MyClientCallException 而不是 ParameterizedTypeReference ? - 这应该允许 jackson 将异常响应流解析为 MyClientCallException

另外,如果您想知道为什么 RestClientException 与其他客户端错误不同 - https://github.com/spring-projects/spring-framework/commit/b84fefc430cdf10d428b02f6d61009b7460ae26f#diff-309e077b6d47c54260c59a899971042456c5e03eb5e96e5121c31842b55deedb