集成测试中 MockMvc 和 RestTemplate 的区别

Posted

技术标签:

【中文标题】集成测试中 MockMvc 和 RestTemplate 的区别【英文标题】:Difference between MockMvc and RestTemplate in integration tests 【发布时间】:2014-11-12 03:55:31 【问题描述】:

MockMvc 和 RestTemplate 都用于与 Spring 和 JUnit 的集成测试。

问题是:它们之间有什么区别,我们什么时候应该选择一个而不是另一个?

这只是两个选项的示例:

//MockMVC example
mockMvc.perform(get("/api/users"))
            .andExpect(status().isOk())
            (...)

//RestTemplate example
ResponseEntity<User> entity = restTemplate.exchange("/api/users",
            HttpMethod.GET,
            new HttpEntity<String>(...),
            User.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());

【问题讨论】:

【参考方案1】:

使用MockMvc,您通常会设置整个 Web 应用程序上下文并模拟 HTTP 请求和响应。因此,尽管一个假的DispatcherServlet 已启动并运行,模拟您的 MVC 堆栈将如何运行,但并没有建立真正的网络连接。

使用RestTemplate,您必须部署一个实际的服务器实例来侦听您发送的 HTTP 请求。

【讨论】:

【参考方案2】:

如this中所说 当你想测试应用程序的服务器端时,你应该使用MockMvc的文章:

Spring MVC 测试建立在来自spring-test 的模拟请求和响应之上,并且不需要正在运行的 servlet 容器。主要区别在于实际的 Spring MVC 配置是通过 TestContext 框架加载的,并且请求是通过实际调用 DispatcherServlet 以及在运行时使用的所有相同的 Spring MVC 基础结构来执行的。

例如:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("servlet-context.xml")
public class SampleTests 

  @Autowired
  private WebApplicationContext wac;

  private MockMvc mockMvc;

  @Before
  public void setup() 
    this.mockMvc = webAppContextSetup(this.wac).build();
  

  @Test
  public void getFoo() throws Exception 
    this.mockMvc.perform(get("/foo").accept("application/json"))
        .andExpect(status().isOk())
        .andExpect(content().mimeType("application/json"))
        .andExpect(jsonPath("$.name").value("Lee"));
  

当你想测试 Rest Client-side 应用程序时应该使用 RestTemplate

如果您有使用 RestTemplate 的代码,您可能需要对其进行测试,并且可以针对正在运行的服务器或模拟 RestTemplate。客户端 REST 测试支持提供了第三种选择,即使用实际的 RestTemplate,但使用自定义的 ClientHttpRequestFactory 对其进行配置,该 ClientHttpRequestFactory 会根据实际请求检查预期并返回存根响应。

示例:

RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);

mockServer.expect(requestTo("/greeting"))
  .andRespond(withSuccess("Hello world", "text/plain"));

// use RestTemplate ...

mockServer.verify();

另请阅读this example

【讨论】:

您在哪里准备名称为Lee 的模拟实体?我认为andExpect(jsonPath("$.name").value("Lee")) 验证会失败。 @naXa,这些是集成测试,所以我们假设相应的数据存储在数据库中或在测试之前填充。 Spring MVC 仅测试“模拟”servlet 容器,但使用真实 bean、数据库连接等构建应用程序上下文。 但是,mockmvc 无法处理存储在文件中的自定义异常(用于测试负面场景)。它捕获了测试用例功能的“抛出异常”。如何在 MockMvc 中处理这个问题?【参考方案3】:

可以同时使用 RestTemplate 和 MockMvc!

如果您有一个单独的客户端,您已经完成了繁琐的 Java 对象到 URL 的映射以及与 Json 之间的转换,并且您希望将其重用于您的 MockMVC 测试,这将非常有用。

这是怎么做的:

@RunWith(SpringRunner.class)
@ActiveProfiles("integration")
@WebMvcTest(ControllerUnderTest.class)
public class MyTestShould 

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void verify_some_condition() throws Exception 

        MockMvcClientHttpRequestFactory requestFactory = new MockMvcClientHttpRequestFactory(mockMvc);
        RestTemplate restTemplate = new RestTemplate(requestFactory);

        ResponseEntity<SomeClass> result = restTemplate.getForEntity("/my/url", SomeClass.class);

        [...]
    


【讨论】:

对于我的用例,我认为这是最好的方法,因为当 HATEOS(特别是)发挥作用时,RestTemplate 使响应的 ORM 映射更加直接。 @fquinner,但它不能回滚,因为它的行为类似于客户端并在不同的线程中运行,无法回滚。然后你需要维护另一个 testDb

以上是关于集成测试中 MockMvc 和 RestTemplate 的区别的主要内容,如果未能解决你的问题,请参考以下文章

MockMVC - 如何使用 org.hamcrest.Matcher 在春季安全集成测试中检查 JWT 令牌的内容

Spring 的 MockMvc 是用于单元测试还是集成测试?

MockMVC 将测试控制器与会话范围 bean 集成

引入MockMVC进行单元测试,可以省去很多麻烦

如何使用 mockMvc 检查响应正文中的字符串

@WithMockUser 在集成测试中不起作用 - Spring boot