Should Mockito be used with MockMvc's webAppContextSetup in Spring 4?

what is mockmvc used for
mockmvc expect exception
unit testing spring mvc controllers example
how to write junit test cases for spring controller using mockito
mockmvc; @mock service
mockmvc perform put
how mockmvc works
kotlin mockmvc

I'm having difficulties getting Mockito and MockMvc working together when I use the webAppContextSetup together. I'm curious if it's because I'm mixing the two in a way they were never intended.

Source: https://github.com/zgardner/spring-boot-intro/blob/master/src/test/java/com/zgardner/springBootIntro/controller/PersonControllerTest.java

Here is the test I'm running:

package com.zgardner.springBootIntro.controller;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static java.lang.Math.toIntExact;
import static org.hamcrest.Matchers.is;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

import com.zgardner.springBootIntro.Application;
import com.zgardner.springBootIntro.service.PersonService;
import com.zgardner.springBootIntro.model.PersonModel;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
public class PersonControllerTest {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Autowired
    private DefaultListableBeanFactory beanFactory;

    @Mock
    private PersonService personService;

    @InjectMocks
    private PersonController personController;

    @Before
    public void setup() {
        initMocks(this);

        // beanFactory.destroySingleton("personController");
        // beanFactory.registerSingleton("personController", personController);

        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    public void getPersonById() throws Exception {
        Long id = 999L;
        String name = "Person name";

        when(personService.findById(id)).thenReturn(new PersonModel(id, name));

        mockMvc.perform(get("/person/getPersonById/" + id))
            .andDo(print())
            .andExpect(jsonPath("$.id", is(toIntExact(id))))
            .andExpect(jsonPath("$.name", is(name)));
    }
}

I was expecting that when mockMvc performed the mock of that HTTP call, it would use the PersonController I defined in my test. But when I debug through, it's using the PersonController which was created by the SpringJunit4ClassRunner on the test boot up.

I found two ways to get this to work:

  1. Inject the bean factory, remove the old personController singleton, and add my own. This is ugly, and I am not a fan.
  2. Wire everything up using the standaloneSetup instead of webAppContextSetup. I may do this instead as I don't have to touch the bean factory.

Here are some different articles I've found that somewhat touch on the topic:

Thoughts?

You might be interested in the new testing features coming in Spring Boot 1.4 (specifically the new @MockBean annotation). This sample shows how a service can be mocked and used with a controller test.

Unit Test Spring MVC Rest Service: MockMVC, JUnit, Mockito, We can use a variety of Matchers to validate if the response is what we expect. org.springframework:spring-test contains MockMvc and other test  On line 4, we use Mockito to define the expected behavior of the mock AccountService.We tell the mock that when it receives an Account it should return 12345.. Lines 6 to 9 use mockMvc to define a

REST Endpoint Testing With MockMvc, Before we can put MockMvc through its paces, we need a REST On line 4, we use Mockito to define the expected behavior of the mock  MockMVC is a companion of the Spring Framework, facilitating testing of Spring Controllers. Mockito is a general purpose mocking framework facilitating unit testing, particularly classes designed with dependency injection in mind.

I sometimes use Mockito to fake Spring beans with usage of @Primary and @Profile annotations. I wrote a blog post about this technique. It also contains link to fully working example hosted on GitHub.

Spring boot MockMVC example with @WebMvcTest, Junit testcases which tests REST controller methods written for Spring boot 2 hateoas example. Spring Framework 3.2 introduces a very elegant way to test Spring MVC controller using MockMvc. Based on the documentation, there are two ways to configure MockMvc:- MockMvcBuilders.webAppContextSetup(webApplicationContext).build() MockMvcBuilders.standaloneSetup(controller).build() The first approach will automatically load the Spring configuration and inject WebApplicationContext into the test.

To extend florent's solution, I encountered performance issues and extensibility issues creating separate configurations for every controller test which needed a different set of service mocks. So instead, I was able to mock out my application's service layer by implementing a BeanPostProcessor alongside my tests which replaces all @Service classes with mocks:

@Component
@Profile("mockService")
public class AbcServiceMocker implements BeanPostProcessor {

  private static final String ABC_PACKAGE = "com.mycompany.abc";

  @Override
  public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
    if (StringUtils.startsWith(bean.getClass().getPackage().getName(), ABC_PACKAGE)) {
      if (AnnotationUtils.isAnnotationDeclaredLocally(Service.class, bean.getClass())
          || AnnotationUtils.isAnnotationInherited(Service.class, bean.getClass())) {
        return mock(bean.getClass());
      }
    }
    return bean;
  }

  @Override
  public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
    return bean;
  }
}

I enabled these mocks in specific tests with an @ActiveProfiles annotation:

@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:/WEB-INF/application-context.xml"})
@ActiveProfiles("mockService")
public class AbcControllerTest {

  private MockMvc mvc;

  @Before
  public final void testBaseSetup() {
    mvc = MockMvcBuilders.webAppContextSetup(context).build();
  }

Lastly, the injected Mockito mocks were wrapped in an AopProxy causing Mockito's expect and verify calls to fail. So I wrote a utility method to unwrap them:

  @SuppressWarnings("unchecked")
  protected <T> T mockBean(Class<T> requiredType) {
    T s = context.getBean(requiredType);
    if (AopUtils.isAopProxy(s) && s instanceof Advised) {
      TargetSource targetSource = ((Advised) s).getTargetSource();
      try {
        return (T) targetSource.getTarget();
      } catch (Exception e) {
        throw new RuntimeException("Error resolving target", e);
      }
    }
    Mockito.reset(s);
    return s;
  }

MockMvc, in integration with a Mocked MVC container with mocked inputs and dependencies. The test pyramid is also used by other authors to describe a test portfolio in terms of cost and speed. The base of the pyramid are unit tests, meaning that unit tests are the foundation of the testing strategy and that there should be many more unit tests than high level end-to-end tests.

Spring MockMvc tutorial, Rather, I use Mockito to mock out all the dependencies within the controller. Granted, we should already have our unit tests for the two  What is mockito? Mockito is a mocking framework that tastes really good. It lets you write beautiful tests with a clean & simple API. Mockito doesn’t give you hangover because the tests are very readable and they produce clean verification errors. — “Mockito.” Mockito Framework Site. N.p., n.d. Web. 28 Apr. 2017.

MockMvc + Mockito = Epic Tests – My Shitty Code, Instead, we'll use an in-memory relational database (H2 DB) for this demo. All we need to do is two things: Include the 'h2database' run-time  That way, almost of the full stack is used, and your code will be called in exactly the same way as if it were processing a real HTTP request but without the cost of starting the server. To do that, use Spring’s MockMvc and ask for that to be injected for you by using the @AutoConfigureMockMvc annotation on the test case

Testing Spring Boot RESTful APIs using MockMvc/Mockito, Test , In this post we'll see how to use MockMvc to test Spring endpoints. tests using Spring MVC test framework should be treated as unit tests and be This annotation will configure the SpringRunner JUnit test runner to deploy  Above when we mocked we used the thenReturn after calling when. 10:27. It's a little different in Mockito and. 10:29. another mocking frameworks when you want to mock avoid method. 10:34. What this means is that we can't use Mockito's when method like we did above. 10:38. No worries though there's another method for that it's called doAnswer. 10:44

Comments
  • Mock beans are the way to go!