当我同时使用 webAppContextSetup 时,我很难让 Mockito 和 MockMvc 一起工作。我很好奇这是否是因为我以一种他们从未想过的方式将两者混合在一起。
这是我正在运行的测试:
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)));
}
}
我期望当 mockMvc 执行该 HTTP 调用的模拟时,它会使用我在测试中定义的 PersonController。但是当我调试通过时,它使用的是在测试启动时由 SpringJunit4ClassRunner 创建的 PersonController。
我找到了两种方法让它工作:
- 注入(inject) bean 工厂,删除旧的 personController 单例,并添加我自己的。这太丑了,我不是粉丝。
- 使用 standaloneSetup 而不是 webAppContextSetup 连接所有内容。我可能会这样做,因为我不必接触 bean 工厂。
以下是我发现的一些不同的文章,它们在一定程度上涉及到这个主题:
- Spring Tutorial - Building REST Services这只是在进行集成测试之前在存储库中 Autowiring 以清除数据。
- Use Spring MVC Test framework and Mockito to test controllers这使用 Mockito 和 webAppContextSetup,但这是在 Spring 3 中。(我使用的是 Spring Boot)
- Unable to mock Service class in Spring MVC Controller tests这使用了 standaloneSetup,它也适用于我的情况。
想法?
请您参考如下方法:
您可能对 new testing features 感兴趣即将在 Spring Boot 1.4 中出现(特别是新的 @MockBean
注释)。这sample展示了如何通过 Controller 测试模拟和使用服务。