我有一个 Spring 3.2 MVC 应用程序,并且正在使用 Spring MVC 测试框架来测试我的控制器操作的 GET 和 POST 请求。我正在使用 Mockito 来模拟服务,但发现模拟被忽略了,并且正在使用我的实际服务层(因此,数据库被命中)。
我的控制器测试中的代码:
package name.hines.steven.medical_claims_tracker.controllers;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import name.hines.steven.medical_claims_tracker.domain.Policy;
import name.hines.steven.medical_claims_tracker.services.PolicyService;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
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;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({ "classpath:/applicationContext.xml", "classpath:/tests_persistence-applicationContext.xml" })
public class PolicyControllerTest {
@Mock
PolicyService service;
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
// this must be called for the @Mock annotations above to be processed.
MockitoAnnotations.initMocks(this);
}
@Test
public void createOrUpdateFailsWhenInvalidDataPostedAndSendsUserBackToForm() throws Exception {
// Post no parameters in this request to force errors
mockMvc.perform(post("/policies/persist")).andExpect(status().isOk())
.andExpect(model().attributeHasErrors("policy"))
.andExpect(view().name("createOrUpdatePolicy"));
}
@Test
public void createOrUpdateSuccessful() throws Exception {
// Mock the service method to force a known response
when(service.save(isA(Policy.class))).thenReturn(new Policy());
mockMvc.perform(
post("/policies/persist").param("companyName", "Company Name")
.param("name", "Name").param("effectiveDate", "2001-01-01"))
.andExpect(status().isMovedTemporarily()).andExpect(model().hasNoErrors())
.andExpect(redirectedUrl("list"));
}
}
你会注意到我有两个上下文配置文件;这是一个 hack,因为如果我无法阻止控制器测试到达实际的服务层,那么该服务层可能会将其存储库指向测试数据库。我还没有到无法再摆脱这种黑客攻击并且需要能够正确模拟我的服务层的地步。
为什么when(service.save(isA(Policy.class))).thenReturn(new Policy());
没有在 PolicyService 中启动和模拟 save 方法?我在某处错过了一些模拟配置吗?我需要在 Spring 配置中添加什么吗?到目前为止,我的研究仅限于谷歌搜索“spring mvc test mockito not working”,但这并没有给我太多的继续。
谢谢。
更新 1
你是对的@tom-verelst,我指的PolicyService service;
是我的测试中的那一行,所以里面的服务MockMvc
当然是由 Spring 注入的。
我做了一些研究,发现一篇博客文章很好地解释了@InjectMocks
它的用途。
然后我尝试使用注释private MockMvc mockMvc
,@InjectMocks
但仍然遇到同样的问题(即,里面的服务没有像我预期的那样MockMvc
被嘲笑)。我在调试过程中调用了 save 方法时添加了堆栈跟踪(与模拟服务中对 save 方法的期望调用相反)。PolicyServiceImpl
Thread [main] (Suspended (breakpoint at line 29 in DomainEntityServiceImpl) PolicyServiceImpl(DomainEntityServiceImpl<T>).save(T) line: 29
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 597
AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) line: 317
ReflectiveMethodInvocation.invokeJoinpoint() line: 183
ReflectiveMethodInvocation.proceed() line: 150
TransactionInterceptor$1.proceedWithInvocation() line: 96
TransactionInterceptor(TransactionAspectSupport).invokeWithinTransaction(Method, Class, TransactionAspectSupport$InvocationCallback) line: 260
TransactionInterceptor.invoke(MethodInvocation) line: 94
ReflectiveMethodInvocation.proceed() line: 172
JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 204
$Proxy44.save(DomainEntity) line: not available
PolicyController.createOrUpdate(Policy, BindingResult) line: 64
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 597
ServletInvocableHandlerMethod(InvocableHandlerMethod).invoke(Object...) line: 219
ServletInvocableHandlerMethod(InvocableHandlerMethod).invokeForRequest(NativeWebRequest, ModelAndViewContainer, Object...) line: 132
ServletInvocableHandlerMethod.invokeAndHandle(ServletWebRequest, ModelAndViewContainer, Object...) line: 104
RequestMappingHandlerAdapter.invokeHandleMethod(HttpServletRequest, HttpServletResponse, HandlerMethod) line: 746
RequestMappingHandlerAdapter.handleInternal(HttpServletRequest, HttpServletResponse, HandlerMethod) line: 687
RequestMappingHandlerAdapter(AbstractHandlerMethodAdapter).handle(HttpServletRequest, HttpServletResponse, Object) line: 80
TestDispatcherServlet(DispatcherServlet).doDispatch(HttpServletRequest, HttpServletResponse) line: 925
TestDispatcherServlet(DispatcherServlet).doService(HttpServletRequest, HttpServletResponse) line: 856
TestDispatcherServlet(FrameworkServlet).processRequest(HttpServletRequest, HttpServletResponse) line: 915
TestDispatcherServlet(FrameworkServlet).doPost(HttpServletRequest, HttpServletResponse) line: 822
TestDispatcherServlet(HttpServlet).service(HttpServletRequest, HttpServletResponse) line: 727
TestDispatcherServlet(FrameworkServlet).service(HttpServletRequest, HttpServletResponse) line: 796
TestDispatcherServlet.service(HttpServletRequest, HttpServletResponse) line: 66
TestDispatcherServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 820
MockFilterChain$ServletFilterProxy.doFilter(ServletRequest, ServletResponse, FilterChain) line: 168
MockFilterChain.doFilter(ServletRequest, ServletResponse) line: 136
MockMvc.perform(RequestBuilder) line: 134
PolicyControllerTest.createOrUpdateSuccessful() line: 67
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 39
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 597
FrameworkMethod$1.runReflectiveCall() line: 44
FrameworkMethod$1(ReflectiveCallable).run() line: 15
FrameworkMethod.invokeExplosively(Object, Object...) line: 41
InvokeMethod.evaluate() line: 20
RunBefores.evaluate() line: 28
RunBeforeTestMethodCallbacks.evaluate() line: 74
RunAfterTestMethodCallbacks.evaluate() line: 83
SpringRepeat.evaluate() line: 72
SpringJUnit4ClassRunner.runChild(FrameworkMethod, RunNotifier) line: 231
SpringJUnit4ClassRunner.runChild(Object, RunNotifier) line: 88
ParentRunner$3.run() line: 193
ParentRunner$1.schedule(Runnable) line: 52
SpringJUnit4ClassRunner(ParentRunner<T>).runChildren(RunNotifier) line: 191
ParentRunner<T>.access$000(ParentRunner, RunNotifier) line: 42
ParentRunner$2.evaluate() line: 184
RunBeforeTestClassCallbacks.evaluate() line: 61
RunAfterTestClassCallbacks.evaluate() line: 71
SpringJUnit4ClassRunner(ParentRunner<T>).run(RunNotifier) line: 236
SpringJUnit4ClassRunner.run(RunNotifier) line: 174
JUnit4TestMethodReference(JUnit4TestReference).run(TestExecution) line: 50
TestExecution.run(ITestReference[]) line: 38
RemoteTestRunner.runTests(String[], String, TestExecution) line: 467
RemoteTestRunner.runTests(TestExecution) line: 683
RemoteTestRunner.run() line: 390
RemoteTestRunner.main(String[]) line: 197
更多研究(Mockito Injecting Null values into a Spring bean when using @Mock?)@InjectMocks
建议将PolicyController
对此一无所知。