mvc集成测试失败

我正在开发一个使用spring boot 1.3的spring应用程序

我有一个MVC请求处理程序,看起来像这样:

@RequestMapping(method = PUT, path = "/{categoryId}")
    public String update(@Valid @ModelAttribute("category") Category category, BindingResult result, @GetAttribute("currentStore") Store store, Model model, RedirectAttributes ra) {
        if (result.hasErrors()) {
            model.addAttribute("categories", categoryService.activeCategories(store));
            return EDIT_VIEW_NAME;
        } else {
            categoryService.update(category);
            ra.addFlashAttribute("info", "Category updated successfully!");
            return redirectTo(categoryUrls.indexPath());
        }
    }

这是它的集成测试

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(EstoreApplication.class)
@WebIntegrationTest
@Transactional
public class CategoriesControllerIntegrationTests {

    @Autowired
    private WebApplicationContext wac;
    private MockMvc mockMvc;


    @Test
    @WithFactoryUser(roles = "admin")
    public void testUpdateCategoryActionWithInvalidData() throws Exception {
        //GIVEN
        String categoryId = "category_001";
        Store store = storeBuilder.getTestStore();
        categoryBuilder.createListWithStore(5,store);
        Category category = categoryBuilder.createWithId(categoryId);

        //WHEN
        this.mockMvc.perform(
                put("/admin/categories/{categoryId}", categoryId)
                        .param("name", category.getName())
                        .param("title", category.getTitle())
                        .param("status", " ")
                        .param("store", store.getId())
                        .with(csrf())
        )

        //THEN
                .andExpect(status().isOk())
                .andExpect(view().name(CategoriesController.EDIT_VIEW_NAME))
                .andExpect(model().attributeHasErrors("category"))
                .andExpect(model().attributeHasFieldErrors("category", "status"))
        ;
    }
}

删除了一些空间,但注意顶部的@Transactional注释

它运行并通过没有线路:

model.addAttribute("categories", categoryService.activeCategories(store));

但是当添加它时,它会给出这个令人讨厌的异常,这是一个没有任何数据插入操作的bean验证异常(验证错误已经由spring mvc处理并且BindingResult中的结果)

List of constraint violations:[
    ConstraintViolationImpl{interpolatedMessage='{com.estore.constraints.inset}', propertyPath=status, rootBeanClass=class org.commerceforge.estore.app.model.Category, messageTemplate='{com.estore.constraints.inset}'}
    ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=status, rootBeanClass=class org.commerceforge.estore.app.model.Category, messageTemplate='{org.hibernate.validator.constraints.NotBlank.message}'}
], mergedContextConfiguration = [WebMergedContextConfiguration@ba2f4ec testClass = CategoriesControllerIntegrationTests, locations = '{}', classes = '{class org.commerceforge.estore.EstoreApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.IntegrationTest=true}', resourceBasePath = '', contextLoader = 'org.springframework.boot.test.SpringApplicationContextLoader', parent = [null]]].

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.validation.ConstraintViolationException: Validation failed for classes [org.commerceforge.estore.app.model.Category] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
    ConstraintViolationImpl{interpolatedMessage='{com.estore.constraints.inset}', propertyPath=status, rootBeanClass=class org.commerceforge.estore.app.model.Category, messageTemplate='{com.estore.constraints.inset}'}
    ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=status, rootBeanClass=class org.commerceforge.estore.app.model.Category, messageTemplate='{org.hibernate.validator.constraints.NotBlank.message}'}
]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:981)
    at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:882)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:651)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:845)
    at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:316)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:126)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:90)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:122)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:48)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:205)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:120)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:120)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:91)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:53)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:213)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:176)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
    at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:155)
    at org.commerceforge.estore.tests.mvc.web.integration.CategoriesControllerIntegrationTests.testUpdateCategoryActionWithInvalidData(CategoriesControllerIntegrationTests.java:296)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:254)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
    at org.junit.runners.Suite.runChild(Suite.java:128)
    at org.junit.runners.Suite.runChild(Suite.java:27)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
Caused by: javax.validation.ConstraintViolationException: Validation failed for classes [org.commerceforge.estore.app.model.Category] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
    ConstraintViolationImpl{interpolatedMessage='{com.estore.constraints.inset}', propertyPath=status, rootBeanClass=class org.commerceforge.estore.app.model.Category, messageTemplate='{com.estore.constraints.inset}'}
    ConstraintViolationImpl{interpolatedMessage='may not be empty', propertyPath=status, rootBeanClass=class org.commerceforge.estore.app.model.Category, messageTemplate='{org.hibernate.validator.constraints.NotBlank.message}'}
]
    at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:160)
    at org.hibernate.cfg.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:95)
    at org.hibernate.action.internal.EntityInsertAction.preInsert(EntityInsertAction.java:218)
    at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:97)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:465)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:351)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350)
    at org.hibernate.event.internal.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:67)
    at org.hibernate.internal.SessionImpl.autoFlushIfRequired(SessionImpl.java:1227)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1293)
    at org.hibernate.internal.QueryImpl.list(QueryImpl.java:103)
    at org.hibernate.jpa.internal.QueryImpl.list(QueryImpl.java:573)
    at org.hibernate.jpa.internal.QueryImpl.getResultList(QueryImpl.java:449)
    at org.hibernate.jpa.criteria.compile.CriteriaQueryTypeQueryAdapter.getResultList(CriteriaQueryTypeQueryAdapter.java:67)
    at org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:114)
    at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:78)
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:100)
    at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:91)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:462)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:440)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:131)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
    at com.sun.proxy.$Proxy163.findAllByStoreAndStatus(Unknown Source)
    at org.commerceforge.estore.app.service.impl.CategoryServiceImpl.activeCategories(CategoryServiceImpl.java:115)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
    at com.sun.proxy.$Proxy200.activeCategories(Unknown Source)
    at org.commerceforge.estore.app.web.mvc.CategoriesController.update(CategoriesController.java:81)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:222)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:814)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:969)
    ... 84 more

当我删除了类级别的@Transactional时,一切都运行顺利,所以我认为这是交易中的一个问题

该行:

model.addAttribute("categories", categoryService.activeCategories(store));

是一个查找器查询的包装器:

@Transactional(readOnly = true)
    public List<Category> activeCategories(Store store) {
        return categoryRepository.findAllByStoreAndStatus(store, Category.STATUS_ENABLED);
    }

它只是一个select quert,它永远不会导致bean验证错误

会发生什么呢,我传递了一组无效数据来测试它不会传递,spring mvc在BindingResult中捕获错误,但似乎在测试方法结束时,某种事务回滚(我不确定,只是猜测)并尝试插入数据

如果我删除了@Transactional注释,但是这对我来说并不实际,因为我的整个测试套件(950多个测试)依赖于空数据库,并且我使用@Transactional实现它以回滚对数据的任何更改


我认为问题是readOnly查询的flush模式。 如果您使用的是休眠模式,您可以通过org.springframework将flush模式设置为COMMIT(https://docs.jboss.org/hibernate/orm/4.3/javadocs/org/hibernate/jpa/QueryHints.html#HINT_FLUSH_MODE) .data.jpa.repository.QueryHints - 您的方法的注释。


我只能在这一点上猜测......等待你对我的问题的回应。 但我认为你有一个@ ModelAttribute注释的方法来查找类别对象(类似于http://www.javabeat.net/modelattribute-spring-mvc/)

这就是为什么Category-object被加载并且被请求中的数据修改后,删除了当提交查询时刷新的entityManager中包含的无效数据的status-value => Category-Object。

这个问题也可以通过调用entityManager.clear()或者entityManager.detach(category)来缓解。


我想你有一个OpenEntityManagerInViewFilter。 你应该知道,即使在你的控制器没有显式调用persist / update的情况下,如果实体是从非readOnly-repository-method中获取的,你所做的每个修改都会被写入数据库,除非有事务回滚(通过引发异常或手动通知事务回滚http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/transaction/TransactionStatus.html#setRollbackOnly%28%29)

...编辑

啊。 这就是为什么测试在没有@ Transactional-rollback的情况下工作的原因,因为在调用@ ModelAttribute-annotated方法时没有事务=> entityManager立即关闭,因此在提交查询时不会刷新Category。

链接地址: http://www.djcxy.com/p/67369.html

上一篇: mvc integration Tests to fail

下一篇: Spring MVC building a collaborative webapp need advice and design examples