【问题标题】:Testing JSON mapping for a Spring Boot RestTemplate client测试 Spring Boot RestTemplate 客户端的 JSON 映射
【发布时间】:2019-07-06 03:31:00
【问题描述】:

我有一个不受我控制的 REST API(由另一个遥远的团队提供),我需要从 Spring Boot 应用程序中使用它。

目前我想编写一个测试,以确保我的 RestTemplate 调用产生的 request(不是响应)与远程端的预期完全对应。我有一个示例 JSON sn-p,我想从我的代码中复制它 - 给定与示例中相同的参数,我应该在请求正文中得到一个等效的 JSON sn-p,然后我想对其进行分析以确定。

到目前为止,我的想法是让 RestTemplate 使用我控制的服务器,然后捕获 JSON 请求。显然MockRestServiceServer 是一个不错的选择。

这是正确的方法吗?如何配置 MockRestServiceServer 以允许我这样做?

【问题讨论】:

  • 所以你想测试第三方服务?如果它关闭了大约 3 周会发生什么?你不会让你的应用程序运行?
  • 你了解依赖服务的API吗?
  • 另外,这是一个集成测试吗?有很多吗?
  • 您可以检查 MockMvc 是否适合您。您可以使用 MockHttpServletRequestBuilder 类中的方法创建请求。
  • @VeselinDavidov 不,我想确保我生成和发送的 JSON 符合预期。我不需要在测试中实际访问远程服务。

标签: java spring-boot junit resttemplate


【解决方案1】:

如果您只对验证 JSON 映射感兴趣,您始终可以直接使用 Jackson 的 ObjectMapper 并使用 JSONassert 之类的库来验证对象结构是否匹配,以验证序列化字符串是否与您的预期结果匹配。例如:

@Autowired
private ObjectMapper objectMapper;
private Resource expectedResult = new ClassPathResource("expected.json");

@Test
public void jsonMatches() {
    Foo requestBody = new Foo();
    String json = objectMapper.writeValueAsString(requestBody);
    String expectedJson = Files
        .lines(expectedResult.getFile())
        .collect(Collectors.joining());
    JSONAssert.assertEquals(expectedJson, json, JSONCompareMode.LENIENT);
}

这个测试纯粹使用ObjectMapper 来验证 JSON 映射,所以您甚至可以在不需要在测试中实际引导 Spring 引导的情况下执行此操作(这可能会更快)。这样做的缺点是,如果您使用的框架与 Jackson 不同,或者 RestTemplate 更改了其实现,则此测试可能会过时。


或者,如果您有兴趣验证完整的请求是否匹配(URL、请求方法、请求正文等),您可以使用您提到的MockRestServiceServer。这可以通过将@SpringBootTest 注释添加到您的测试、自动装配RestTemplate 和调用RestTemplate 的服务来完成,例如:

@RunWith(SpringRunner.class)
@SpringBootTest
public class FooServiceTests {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private FooService fooService; // Your service

    private MockRestServiceServer server;

    @Before
    public void setUp() {
        server = MockRestServiceServer.bindTo(restTemplate).build();
    }
}

然后您可以使用以下命令设置测试:

@Test
public void postUsesRestTemplate() throws IOException, URISyntaxException {
    Path resource = Paths.get(getClass().getClassLoader().getResource("expected-foo.json").toURI());
    String expectedJson = Files.lines(resource).collect(Collectors.joining());
    server.expect(once(), requestTo("http://example.org/api/foo"))
        .andExpect(method(HttpMethod.POST))
        .andExpect(MockRestRequestMatchers.content().json(expectedJson))
        .andRespond(withSuccess());
    // Invoke your service here
    fooService.post();
    server.verify();
}

【讨论】:

  • 是的。我想验证 RestTemplate 使用的 JSON 映射 实际。据我了解,这并没有做到这一点?
  • @ThorbjørnRavnAndersen 此测试验证 Jackson 是否为您的对象创建了正确的 JSON 字符串。考虑到 RestTemplate 使用消息转换器列表,并且默认情况下将使用 Jackson 创建 JSON 字符串,因此该测试应该产生相同的结果。
  • @ThorbjørnRavnAndersen 我还提供了一个带有MockRestServiceServer 的示例,这就是您要查找的内容。
  • 我最终使用了这两种方法,首先让 AutoValue bean 正确映射,然后实际进行测试。很有帮助,谢谢!
【解决方案2】:

根据文档,您可以在 Mock 上使用 json 路径匹配请求。例如;

RestTemplate restTemplate = new RestTemplate()
 MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate).build();

server.expect(ExpectedCount.once(), requestTo(path))
                .andExpect(method(HttpMethod.POST))
                .andExpect(jsonPath("$", hasSize(1)))
                .andExpect(jsonPath("$[0].someField").value("some value"))

注意:我还没有测试过这个。

但是我已经多次使用 Wire Mock 实现了您想要的。这又是一个比MockRestServiceServer 更好的选择。为什么这么说?

  • 广泛采用和支持
  • 更优雅、更广泛的请求和响应匹配
  • 高度可配置
  • 录制和回放
  • 可配置的安全/身份验证
  • 你甚至可以将这个 dockerise 化

看看http://wiremock.org/docs/request-matching/

【讨论】:

  • 有趣。 WireMock 是使用网络端口还是完全内部的?
  • 它确实允许您配置端口 - wiremock.org/docs/configuration
  • 如果可以的话,我不想使用端口。
  • 嗯,默认http端口是8080
【解决方案3】:

如果您想手动检查一次,我认为您使用存根服务器的方法(您可以为此使用 WireMock)很好。

或者,您可以将request logger 添加到记录每个请求的 RestTemplate 中。如果出现问题,这将更容易检查发送的请求是否正确。

【讨论】:

  • 我希望这最终成为我的解决方案中的回归测试,以帮助未来的维护者。
  • 如果您根据静态值检查请求,您将不会注意到远程服务是否发生更改。这里最好的回归测试是调用实际服务的集成测试(例如,如果可能,在他们的验收环境中)并验证结果。如果请求不正确,那么大概响应也不会 - 最终这不是最重要的吗?
  • 是的。我需要的回归测试是我的解决方案中的 JSON 映射是否始终保持 100% 准确于提供的文档,即使在进行大量重构(或尝试)时也是如此。如果没有适当的通知,远程端(可能)不会改变,所以这不是我想要确保的。
猜你喜欢
  • 2016-07-17
  • 2019-04-28
  • 1970-01-01
  • 1970-01-01
  • 2018-04-26
  • 2020-08-08
  • 2018-05-12
  • 2015-08-26
  • 2022-11-28
相关资源
最近更新 更多