Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inject mock into Spring MockMvc WebApplicationContext

I'm working to test (via JUnit4 and Spring MockMvc) a REST service adapter using Spring-boot. The adapter simply passes along requests made to it, to another REST service (using a custom RestTemplate) and appends additional data to the responses.

I'd like to run MockMvc tests to perform controller integration tests, but want to override the RestTemplate in the controller with a mock to allow me to predefine the 3rd party REST response and prevent it from being hit during each test. I've been able to accomplish this by instantiating a MockMvcBuilders.standAloneSetup() and passing it the controller to be tested with the mock injected as listed in this post (and my setup below), however I am not able to do the same using MockMvcBuilders.webAppContextSetup().

I've been through a few other posts, none of which answer the question as to how this might be accomplished. I would like to use the actual Spring application context for the tests instead of a standalone to prevent any gaps as the application is likely to grow.

EDIT: I am using Mockito as my mocking framework and am trying to inject one of its mocks into the context. If this isn't necessary, all the better.

Controller:

@RestController
@RequestMapping(Constants.REQUEST_MAPPING_PATH)
public class Controller{

    @Autowired
    private DataProvider dp;    

    @Autowired
    private RestTemplate template;

    @RequestMapping(value = Constants.REQUEST_MAPPING_RESOURCE, method = RequestMethod.GET)
    public Response getResponse(
            @RequestParam(required = true) String data,
            @RequestParam(required = false, defaultValue = "80") String minScore
            ) throws Exception {

        Response resp = new Response();

        // Set the request params from the client request
        Map<String, String> parameters = new HashMap<String, String>();
        parameters.put(Constants.PARAM_DATA, data);
        parameters.put(Constants.PARAM_FORMAT, Constants.PARAMS_FORMAT.JSON);

        resp = template.getForObject(Constants.RESTDATAPROVIDER_URL, Response.class, parameters);

        if(resp.getError() == null){
            resp.filterScoreLessThan(new BigDecimal(minScore));
            new DataHandler(dp).populateData(resp.getData());
        }
        return resp;
    }
}

Test class:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@SpringApplicationConfiguration(classes = MainSpringBootAdapter.class)
@TestPropertySource("/application-junit.properties")
public class WacControllerTest {

    private static String controllerURL = Constants.REQUEST_MAPPING_PATH + Constants.REQUEST_MAPPING_RESOURCE + compressedParams_all;
    private static String compressedParams_all = "?data={data}&minScore={minScore}";

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @InjectMocks
    private Controller Controller;

    @Mock
    private RestTemplate rt;

    @Value("${file}")
    private String file;

    @Spy
    private DataProvider dp;

    @Before
    public void setup() throws Exception {
        dp = new DataProvider(file);    
        MockitoAnnotations.initMocks(this);
        this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test
    public void testGetResponse() throws Exception {

        String[] strings = {"requestData", "100"};

        Mockito.when(
            rt.getForObject(Mockito.<String> any(), Mockito.<Class<Object>> any(), Mockito.<Map<String, ?>> any()))
            .thenReturn(populateTestResponse());

        mockMvc.perform(get(controllerURL, strings)
            .accept(Constants.APPLICATION_JSON_UTF8))
            .andDo(MockMvcResultHandlers.print());

        Mockito.verify(rt, Mockito.times(1)).getForObject(Mockito.<String> any(), Mockito.<Class<?>> any(), Mockito.<Map<String, ?>> any());

        }


        private Response populateTestResponse() {
            Response  resp = new Response();

            resp.setScore(new BigDecimal(100));
            resp.setData("Some Data");

            return resp;
    }
}
like image 605
ethesx Avatar asked Aug 04 '15 21:08

ethesx


People also ask

What is WebApplicationContext in spring?

What is WebApplicationContext in Spring MVC? WebApplicationContext in Spring is a web-aware ApplicationContext i.e it has Servlet Context information. In a single web application, there can be multiple WebApplicationContext. That means each DispatcherServlet is associated with a single WebApplicationContext.

Should I use MockMvc?

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.

Is MockMvc integration test?

Technically speaking, tests using MockMvc are in the boundaries between unit and integration tests. They aren't unit tests because endpoints are tested in integration with a Mocked MVC container with mocked inputs and dependencies.


1 Answers

Spring's MockRestServiceServer is exactly what you're looking for.

Short description from javadoc of the class:

Main entry point for client-side REST testing. Used for tests that involve direct or indirect (through client code) use of the RestTemplate. Provides a way to set up fine-grained expectations on the requests that will be performed through the RestTemplate and a way to define the responses to send back removing the need for an actual running server.

Try to set up your test like this:

@WebAppConfiguration
@ContextConfiguration(classes = {YourSpringConfig.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class ExampleResourceTest {

    private MockMvc mockMvc;
    private MockRestServiceServer mockRestServiceServer;

    @Autowired
    private WebApplicationContext wac;

    @Autowired
    private RestOperations restOperations;

    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
        mockRestServiceServer = MockRestServiceServer.createServer((RestTemplate) restOperations);
    }


    @Test
    public void testMyApiCall() throws Exception {
        // Following line verifies that our code behind /api/my/endpoint made a REST PUT
        // with expected parameters to remote service successfully
        expectRestCallSuccess();

        this.mockMvc.perform(MockMvcRequestBuilders.get("/api/my/endpoint"))
            .andExpect(status().isOk());
    }

    private void expectRestCallSuccess() {
        mockRestServiceServer.expect(
            requestTo("http://remote.rest.service/api/resource"))
            .andExpect(method(PUT))
            .andRespond(withSuccess("{\"message\": \"hello\"}", APPLICATION_JSON));
    }


}
like image 89
mzc Avatar answered Sep 19 '22 19:09

mzc