javarestspring-mvcmockitospring-test-mvc

Use MockMVC outside SpringBoot application


I have an application that uses Spring MVC for running a REST service (without Spring Boot). Context is mainly loaded from the parent's one. I have a controller and I would like to test it via MockMvc.

I have tried to set up local test context manually, but it wasn't enough for running tests. I assume, there should be extra beans I haven't set up.

My controller is:

@RestController
public class ProrertyEditorController extends AbstractPropertyEditorController {
     
    @Autowired
    protected PropertyEditorService prorertyEditorService;

    @RequestMapping(method = RequestMethod.DELETE, value = "/{dataType}/deletewithcontent")
    @ResponseStatus(value = HttpStatus.OK)
    public void deleteWithContent(@PathVariable("dataType") String dataType, @RequestParam("deleteall") boolean deleteAllContent, @RequestBody String node) {
        try {
            JSONArray itemsToDelete = new JSONArray(node);
            prorertyEditorService.deleteItemsWithContent(dataType, itemsToDelete, deleteAllContent);
        } catch (Exception e) {
        //handling exception
        }
    }
}

Up to this moment, test for controller looks like this:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("classpath*:configBeans1.xml")
public class ProrertyEditorControllerTest{
    private MockMvc mockMvc;

    @Mock
    private PropertyEditorService mockService;
    @InjectMocks
    private ProrertyEditorController controller;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.standaloneSetup(new ProrertyEditorController()).build();
    }

    @Test
    public void deleteWithContentTest() throws Exception {
        mockMvc.perform(delete("/full/path/{dataType}/deletewithcontent", type)
                .param("deleteall", "true")
                .param("node", "[{\"test key1\":\"test value1\"}, {\"test keys2\":\"test value2\"}]"));

        verify(mockService, times(1)).deleteItemsWithContent(eq("promotion"), eq(new JSONArray("[{\"test key1\":\"test value1\"}, {\"test keys2\": \"test value2\"}]")), eq(true));
    }
}

Unfortunately, it doesn't work due to Failed to load ApplicationContext and no bean is created.

PS There is an option to use

MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();

However it may require refactoring of controllers method.


Solution

  • It turned out that it is absolutely possible to do it. There are just a few configs required to launch it.

    1. You will need spring-test in your pom.xml:

       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-test</artifactId>
           <scope>test</scope>
       </dependency>
      
    2. Create a testContext.xml file. In my case, it was literally empty:

       <?xml version="1.0" encoding="UTF-8"?>
       <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans.xsd">
      
       </beans>
      

      Nevertheless, it is still required, otherwise, MockMVC will not start since there is no context.

    3. Config your ControllerTest class with the following annotations:

       @RunWith(SpringJUnit4ClassRunner.class)
       @ContextConfiguration(locations = "classpath*:testContextConfig.xml")
       @WebAppConfiguration
       public class ControllerTest {        ...    }
      

      Without @ContextConfiguration the MockMVC won't work.

    4. Create a MockMVC instance in @Before method:

       private MockMvc mockMvc;
      
       @Mock
       private Service mockService;
      
       @Before
       public void setup() {
           MockitoAnnotations.initMocks(this);
           mockMvc = MockMvcBuilders.standaloneSetup(new Controller(mockService))
                   .setHandlerExceptionResolvers(exceptionResolver()) //crucial for standaloneSetup of MockMVC
                   .build();
       }
      

      As far as I've understood, setHandlerExceptionResolvers is a crucial part of mockMVC setup.