87

Both MockMvc and RestTemplate are used for integration tests with Spring and JUnit.

Question is: what's the difference between them and when we should choose one over another?

Here are just examples of both options:

//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());
Christian Hujer
  • 17,035
  • 5
  • 40
  • 47
Denis C de Azevedo
  • 6,276
  • 2
  • 32
  • 49

3 Answers3

69

As said in this article you should use MockMvc when you want to test Server-side of application:

Spring MVC Test builds on the mock request and response from spring-test and does not require a running servlet container. The main difference is that actual Spring MVC configuration is loaded through the TestContext framework and that the request is performed by actually invoking the DispatcherServlet and all the same Spring MVC infrastructure that is used at runtime.

for example:

@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"));
  }}

And RestTemplate you should use when you want to test Rest Client-side application:

If you have code using the RestTemplate, you’ll probably want to test it and to that you can target a running server or mock the RestTemplate. The client-side REST test support offers a third alternative, which is to use the actual RestTemplate but configure it with a custom ClientHttpRequestFactory that checks expectations against actual requests and returns stub responses.

example:

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

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

// use RestTemplate ...

mockServer.verify();

also read this example

Christian Hujer
  • 17,035
  • 5
  • 40
  • 47
nivash
  • 951
  • 11
  • 17
  • where do you prepare mock entity with name `Lee`? I think `andExpect(jsonPath("$.name").value("Lee"))` validation will fail. – naXa stands with Ukraine Sep 30 '18 at 10:24
  • 1
    @naXa, these are integration tests, so we assume that corresponding data is stored in the database or populated before the test. Spring MVC Test "mocks" servlet container only but build application context with real beans, db connections, etc. – nivash Oct 01 '18 at 08:22
  • But, mockmvc can't handle custom exception(for testing negative scenario) which are stored in a file. It catched with "throws Exception" of Test Case function. How to handle this in MockMvc? – Satish Patro Aug 26 '19 at 04:31
60

With MockMvc, you're typically setting up a whole web application context and mocking the HTTP requests and responses. So, although a fake DispatcherServlet is up and running, simulating how your MVC stack will function, there are no real network connections made.

RestTemplate can conveniently be initialized with a custom ClientHttpRequestFactory. Implementations usually create ClientHttpRequest objects that open actual TCP/HTTP(s) connections. But you don't have to. You can provide a mock implementation where you can do whatever you want. In fact, this is how the MockRestServiceServer utility operates, you could use that.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
21

It is possible to use both RestTemplate and MockMvc!

This is useful if you have a separate client where you already do the tedious mapping of Java objects to URLs and converting to and from Json, and you want to reuse that for your MockMVC tests.

Here is how to do it:

@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);

        [...]
    }

}
Michael Böckling
  • 7,341
  • 6
  • 55
  • 76
  • 3
    For my use case, I think this is the best approach since RestTemplate makes ORM mapping of the response much more straightforward when HATEOS (in particular) comes into play. – fquinner Apr 20 '17 at 16:09
  • @fquinner, but it can't be rollback as it behaves like client and runs in different thread, rollback is not possible. You need to maintain another testDb then – Satish Patro Aug 26 '19 at 04:33