Should Mockito be used with MockMvc's webAppContextSetup in Spring 4?
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.
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:
- Inject the bean factory, remove the old personController singleton, and add my own. This is ugly, and I am not a fan.
- 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:
- Spring Tutorial - Building REST Services This just autowires in the repos to clear out the data before the integration test takes place.
- Use Spring MVC Test framework and Mockito to test controllers This uses Mockito along with webAppContextSetup, but this is in Spring 3. (I'm using Spring Boot)
- Unable to mock Service class in Spring MVC Controller tests This uses the standaloneSetup, which does work in my case too.
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!