我有一个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"));
}
}
你会注意到我有两个上下文配置文件;这是一种黑客行为,因为如果我无法阻止控制器测试命中实际的服务层,那么该服务层的存储库也可能指向测试数据库。我再也不能忍受这种黑客行为了,我需要能够正确地模拟我的服务层。
为什么在(service.save(isA(Policy.class)). then返回(新策略());
没有启动并嘲笑策略服务中的保存方法?我是不是遗漏了什么mockito配置?我需要在Spring配置中添加一些内容吗?到目前为止,我的研究仅限于谷歌搜索“Springmvc测试mockito不起作用”,但这并没有给我太多继续下去的机会。
谢谢
你说得对@tom verelst,我指的是保单服务
行,所以
MockMvc
中的服务当然会在Spring之前注入。
我做了一些研究,发现了一篇博文,很好地解释了
@InjectMocks
的用途。
然后我尝试注释
私有MockMvc mockMvc
与@InjectMocks
,仍然得到了相同的问题(即MockMvc
内的服务没有像我预期的那样被嘲笑)。我在调试期间添加了堆栈跟踪,其中调用了PolcyServiceImpl
上的保存方法(与对模拟服务中的保存方法的所需调用相反)。
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在使用@Mock时向Spring bean注入空值?)建议将
@InjectMocks
应用到测试中的政策控制器
成员变量,但是正如第一个链接中的一个答案所指出的,这没有任何作用,因为Spring对此一无所知。
本节是Spring文档11中的11.3.6 Spring MVC测试框架。测试谈论它,但在某种程度上并不明确。
让我们继续文档中的示例进行解释。示例测试类如下所示
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("test-servlet-context.xml")
public class AccountTests {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Autowired
private AccountService accountService;
// ...
}
假设你有组织。实例AppController作为控制器。在测试servlet上下文中。xml,您将需要
<bean class="org.example.AppController">
<property name="accountService" ref="accountService" />
</bean>
<bean id="accountService" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.example.AccountService"/>
</bean>
该文档缺少控制器的接线部分。如果您使用字段注入,则需要更改为accountService的setter注入。另外,请注意构造函数arg的值(此处为org.example.AccountService)是一个接口,而不是一个类。
在AccountTests中的设置方法中,您将
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
// You may stub with return values here
when(accountService.findById(1)).thenReturn(...);
}
测试方法可能看起来像
@Test
public void testAccountId(){
this.mockMvc.perform(...)
.andDo(print())
.andExpect(...);
}
andDo(print())很方便,比如“import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;”。
我更喜欢Mockmvc的独立服务
提到我的工作
public class AccessControllerTest {
private MockMvc mockMvc;
@Mock
private AccessControlService accessControlService;
@InjectMocks
private AccessController accessController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(accessController).build();
}
@Test
public void validAccessControlRequest() throws Exception {
Bundle bundle = new Bundle();
bundle.setAuthorized(false);
Mockito.when(accessControlService.retrievePatient(any(String.class)))
.thenReturn(bundle);
mockMvc.perform(get("/access/user?user=3")).andExpect(status().isOk());
}
多亏了@J安迪的思路,我意识到我在这件事上走错了方向。在更新1中,我试图将模拟服务注入MockMvc
中,但在后退一步后,我意识到测试的不是MockMvc
,而是我想要测试的PolicyController
。
为了给一点背景知识,我想避免在我的Spring MVC应用程序中对@Controller进行传统的单元测试,因为我想测试那些只通过在Spring本身中运行控制器来提供的东西(例如:RESTful调用控制器动作)。这可以通过使用Spring MVC测试框架来实现,该框架允许您在Spring中运行测试。
您将从我最初问题中的代码中看到,我在一个WebApplicationContext
(即this.mockMvc=MockMvcBuilders.webAppContextSetup(this.wac))中运行Spring MVC测试。build()
)而我应该做的是独立运行。运行standalone允许我直接注入我想要测试的控制器,因此可以控制如何将服务注入控制器(即强制使用模拟服务)。
这在代码中更容易解释。所以对于以下控制器:
import javax.validation.Valid;
import name.hines.steven.medical_claims_tracker.domain.Benefit;
import name.hines.steven.medical_claims_tracker.domain.Policy;
import name.hines.steven.medical_claims_tracker.services.DomainEntityService;
import name.hines.steven.medical_claims_tracker.services.PolicyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/policies")
public class PolicyController extends DomainEntityController<Policy> {
@Autowired
private PolicyService service;
@RequestMapping(value = "persist", method = RequestMethod.POST)
public String createOrUpdate(@Valid @ModelAttribute("policy") Policy policy, BindingResult result) {
if (result.hasErrors()) {
return "createOrUpdatePolicyForm";
}
service.save(policy);
return "redirect:list";
}
}
我现在有了以下测试类,其中服务被成功地模拟出来,我的测试数据库不再被命中:
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.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:/applicationContext.xml" })
public class PolicyControllerTest {
@Mock
PolicyService policyService;
@InjectMocks
PolicyController controllerUnderTest;
private MockMvc mockMvc;
@Before
public void setup() {
// this must be called for the @Mock annotations above to be processed
// and for the mock service to be injected into the controller under
// test.
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controllerUnderTest).build();
}
@Test
public void createOrUpdateFailsWhenInvalidDataPostedAndSendsUserBackToForm() throws Exception {
// POST no data to the form (i.e. an invalid POST)
mockMvc.perform(post("/policies/persist")).andExpect(status().isOk())
.andExpect(model().attributeHasErrors("policy"))
.andExpect(view().name("createOrUpdatePolicy"));
}
@Test
public void createOrUpdateSuccessful() throws Exception {
when(policyService.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"));
}
}
说到Spring,我仍然在学习,所以任何能改进我解释的评论都会受到欢迎。这篇博文对我提出这个解决方案很有帮助。
问题内容: 我有一个ParseService,我想对其进行模拟以测试使用它的所有控制器,我一直在阅读有关茉莉花间谍的信息,但对我来说仍然不清楚。谁能给我一个关于如何模拟定制服务并在Controller测试中使用它的示例吗? 现在,我有一个使用服务插入书的控制器: 服务是这样的: 到目前为止,我的测试如下所示: 现在测试失败: 我做错了什么? 问题答案: 我做错的是没有在beforeEach中将模拟
问题内容: 我有以下情况: controller.js controllerSpec.js 错误: 我也尝试过类似的方法,但没有成功: 我该如何解决?有什么建议? 问题答案: 有两种方法(或肯定有更多方法)。 想象一下这种服务(无论它是工厂都没关系): 使用此控制器: 一种方法是使用要使用的方法创建对象并对其进行监视: 然后,将其作为dep传递给控制器。无需注入服务。那可行。 另一种方法是模拟
> 解析某些文件的服务 管理文件系统的ServiceB 我想测试ControllerClass,特别是:
我正在寻找一种方法来模拟Controller中使用的服务bean,这样我就可以使用MockMVC只测试Controller。但是我找不到一个简单的方法来用Spock Mock代替real bean。一切都使用spring-boot 1.3.2版本。更多细节如下: 我有一个以下控制器类 和集成Spock测试: 我需要一种方法来替换这个autowired bean,用一个mock/stub这样我就可以
我试图在Spring boot 2中编写一个测试类,其中: 我想测试一个控制器 我想嘲笑一个仓库 我想按原样注入一个服务(即不嘲笑它) 该类看起来像: 的(唯一)实现是用注释的,并允许通过其构造函数注入仓库: 运行测试时,我得到了一个,大致上说是“没有可用”。 我怀疑我可能需要一个特定的测试配置来获得服务,但是我被可用的在线文献弄糊涂了。 有指针吗?
在Spring Boot(Spring MVC)中,我试图根据本问题中的#4测试表单到控制器的绑定。我在复制魔法帖请求时遇到问题- 然而,我的断言失败了,itemModel。getId()返回null。当spring调用@Controller类上的方法时,我如何像spring那样初始化模型? 更新 我已经更新了以下内容,但它仍然不起作用: