Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Springs MockMvc returning empty content in one of two only slightly different approaches

I'm testing a Spring MVC @RestController which in turn makes a call to an external REST service. I use MockMvc to simulate the spring environment but I expect my controller to make a real call to the external service. Testing the RestController manually works fine (with Postman etc.).

I found that if I setup the test in a particular way I get a completely empty response (except the status code):

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = AnywhereController.class)
public class AnywhereControllerTest{

    @Autowired
    private AnywhereController ac;

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

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

    @Test
    public void testGetLocations() throws Exception {

        ...

        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get("/anywhere/locations").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(containsString("locations")))
                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON));
        .andReturn();
    } 

The test fails because the content and headers are empty. Then I tried adding this to the test class:

@Configuration
@EnableWebMvc
public static class TestConfiguration{
    @Bean
    public AnywhereController anywhereController(){
        return new AnywhereController();
    }
}

and additionally I changed the ContextConfiguration annotation (although I'd like to know what this actually does):

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration
public class AnywhereControllerTest{...}

Now suddenly all the checks succeed and when printing the content body I'm getting all the content.

What is happening here? What is the difference between these two approaches?

like image 541
Shady Avatar asked Jun 30 '17 23:06

Shady


People also ask

What is MockMvc in spring?

MockMVC class is part of Spring MVC test framework which helps in testing the controllers explicitly starting a Servlet container. In this MockMVC tutorial, we will use it along with Spring boot's WebMvcTest class to execute Junit testcases which tests REST controller methods written for Spring boot 2 hateoas example.

What does MockMvc perform do?

The MockMvc instance is used to perform GET request that expects a JSON response. We check the response for a 200 'OK' response code, a JSON content type and a JSON response body containing the requested account.


1 Answers

Someone in the comments mentioned @EnableWebMvc and it turned out this was the right lead. I wasn't using @EnableWebMvc and therefore

If you don't use this annotation you might not initially notice any difference but things like content-type and accept header, generally content negotiation won't work. Source

My knowledge about the inner workings of the framework is limited but it a simple warning during startup could potentially save many hours of debugging. Chances are high that when people use @Configuration and/or @RestController that they also want to use @EnableWebMvc (or the xml version of it).

Making things even worse, Spring Boot for example adds this annotation automatically, which is why many tutorials on the internet (also the official ones) don't mention @EnableWebMvc.

like image 110
Shady Avatar answered Oct 02 '22 11:10

Shady