在集成测试中,MockMvc和RestTemplate有什么区别?

89

MockMvcRestTemplate都用于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());
3个回答

69

这篇文章所述,当您想要测试应用程序的服务器端(Server-side)时,应使用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().contentType("application/json"))
        .andExpect(jsonPath("$.name").value("Lee"));
  }}

如果你要测试REST客户端应用程序,那么你应该使用RestTemplate

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

例如:

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

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

// use RestTemplate ...

mockServer.verify();

也请阅读这个例子


你在哪里准备名为Lee的模拟实体?我认为andExpect(jsonPath("$.name").value("Lee"))的验证将会失败。 - naXa stands with Ukraine
1
@naXa,这些是集成测试,因此我们假设相应的数据已存储在数据库中或在测试之前填充。Spring MVC测试仅“模拟”Servlet容器,但使用真实的bean、数据库连接等构建应用程序上下文。 - nivash
但是,MockMvc无法处理存储在文件中的自定义异常(用于测试负面场景)。它通过测试用例函数的“throws Exception”捕获。如何在MockMvc中处理这个问题? - Satish Patro

61
使用 MockMvc,通常需要设置整个Web应用程序上下文并模拟HTTP请求和响应。因此,虽然有一个虚假的DispatcherServlet在运行,模拟你的MVC堆栈将如何运行,但没有进行任何真正的网络连接。 RestTemplate可以方便地使用自定义ClientHttpRequestFactory初始化。实现通常创建ClientHttpRequest对象,打开实际的TCP / HTTP(s)连接。但你不必这样做。你可以提供一个模拟实现,在其中可以执行任何操作。实际上,这就是MockRestServiceServer实用程序的操作方式,你可以使用它。

21

可以同时使用 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);

        [...]
    }

}

3
根据我的使用情况,我认为这是最好的方法,因为当HATEOS(尤其是)发挥作用时,RestTemplate使响应的ORM映射更加直观易懂。 - fquinner
@fquinner,但是它不能回滚,因为它的行为类似于客户端并在不同的线程中运行,所以无法回滚。您需要维护另一个testDb。 - Satish Patro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接