在春季测试中请求范围豆
我想在我的应用程序中使用请求范围的bean。 我使用JUnit4进行测试。 如果我尝试在这样的测试中创建一个:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" }) public class TestScopedBeans { protected final static Logger logger = Logger .getLogger(TestScopedBeans.class); @Resource private Object tObj; @Test public void testBean() { logger.debug(tObj); } @Test public void testBean2() { logger.debug(tObj); }
使用以下bean定义:
我得到:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gov.nasa.arc.cx.sor.query.TestScopedBeans': Injection of resource fields failed; nested exception is java.lang.IllegalStateException: No Scope registered for scope 'request' Caused by: java.lang.IllegalStateException: No Scope registered for scope 'request'
所以我发现这个博客看起来很有帮助: http : //www.javathinking.com/2009/06/no-scope-registered-for-scope-request_5.html
但我注意到他使用的是AbstractDependencyInjectionSpringContextTests ,它似乎在Spring 3.0中已被弃用。 我现在使用Spring 2.5,但是认为切换这个方法以使用AbstractJUnit4SpringContextTests作为文档建议应该不会太难(确定文档链接到3.8版本但我使用的是4.4)。 所以我改变测试以扩展AbstractJUnit4SpringContextTests …相同的消息。 同样的问题。 现在我想要覆盖的prepareTestInstance()方法没有定义。 好吧,也许我会将那些registerScope调用放在其他地方……所以我阅读了更多关于TestExecutionListeners的内容,并认为这样会更好,因为我不想inheritancespring包结构。 所以我把我的测试改为:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" }) @TestExecutionListeners({}) public class TestScopedBeans {
期待我必须创建一个自定义监听器,但是当我运行它时。 有用! 很好,但为什么呢? 我没有看到任何股票监听者在哪里注册请求范围或会话范围,为什么会这样? 没有什么可说的我想要的,这可能不是Spring MVC代码的测试……
测试通过,因为它没有做任何事情:)
当省略@TestExecutionListeners
注释时,Spring会注册3个默认侦听器,包括一个名为DependencyInjectionTestExecutionListener
侦听器。 这是负责扫描测试类以寻找要注入的内容的监听器,包括@Resource
注释。 由于未定义的范围,此侦听器尝试注入tObj
并失败。
当您声明@TestExecutionListeners({})
,您禁止注册DependencyInjectionTestExecutionListener
,因此测试永远不会被tObj
注入,并且因为您的测试没有检查是否存在tObj
,所以它会通过。
修改您的测试,以便它执行此操作,它将失败:
@Test public void testBean() { assertNotNull("tObj is null", tObj); }
因此,对于空的@TestExecutionListeners
,测试通过,因为没有任何反应 。
现在,关于你原来的问题。 如果您想尝试使用测试上下文注册请求范围,那么请查看WebApplicationContextUtils.registerWebApplicationScopes()
的源代码,您将找到以下行:
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
你可以尝试一下,看看你怎么做,但可能会有奇怪的副作用,因为你并不是真的想在测试中这样做。
相反,我建议改写您的测试,以便您不需要请求范围的bean。 这应该不难,如果你编写自包含的测试,@ @Test
的生命周期不应该长于请求范围的bean的生命周期。 记住,没有必要测试作用域机制,它是Spring的一部分,你可以认为它有效。
Spring 3.2或更新版本的解决方案
从版本3.2开始的Spring 为集成测试提供了对会话/请求范围bean的支持 。
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = TestConfig.class) @WebAppConfiguration public class SampleTest { @Autowired WebApplicationContext wac; @Autowired MockHttpServletRequest request; @Autowired MockHttpSession session; @Autowired MySessionBean mySessionBean; @Autowired MyRequestBean myRequestBean; @Test public void requestScope() throws Exception { assertThat(myRequestBean) .isSameAs(request.getAttribute("myRequestBean")); assertThat(myRequestBean) .isSameAs(wac.getBean("myRequestBean", MyRequestBean.class)); } @Test public void sessionScope() throws Exception { assertThat(mySessionBean) .isSameAs(session.getAttribute("mySessionBean")); assertThat(mySessionBean) .isSameAs(wac.getBean("mySessionBean", MySessionBean.class)); } }
阅读更多: 请求和会话范围豆
带有监听器的3.2之前的Spring解决方案
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = TestConfig.class) @TestExecutionListeners({WebContextTestExecutionListener.class, DependencyInjectionTestExecutionListener.class, DirtiesContextTestExecutionListener.class}) public class SampleTest { ... }
WebContextTestExecutionListener.java
public class WebContextTestExecutionListener extends AbstractTestExecutionListener { @Override public void prepareTestInstance(TestContext testContext) { if (testContext.getApplicationContext() instanceof GenericApplicationContext) { GenericApplicationContext context = (GenericApplicationContext) testContext.getApplicationContext(); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new SimpleThreadScope()); beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SimpleThreadScope()); } } }
带有自定义范围的3.2之前的Spring解决方案
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = TestConfig.class, locations = "test-config.xml") public class SampleTest { ... }
TestConfig.java
@Configuration @ComponentScan(...) public class TestConfig { @Bean public CustomScopeConfigurer customScopeConfigurer(){ CustomScopeConfigurer scopeConfigurer = new CustomScopeConfigurer(); HashMap scopes = new HashMap(); scopes.put(WebApplicationContext.SCOPE_REQUEST, new SimpleThreadScope()); scopes.put(WebApplicationContext.SCOPE_SESSION, new SimpleThreadScope()); scopeConfigurer.setScopes(scopes); return scopeConfigurer }
或者使用xml配置
test-config.xml
源代码
所有呈现解决方案的源代码:
我尝试了几种解决方案,包括带有“WebContextTestExecutionListener”的@ Marius解决方案,但它对我不起作用,因为此代码在创建请求范围之前加载了应用程序上下文。
最终帮助我的答案不是新的,但它很好: http : //tarunsapra.wordpress.com/2011/06/28/junit-spring-session-and-request-scope-beans/
我只是将以下代码段添加到我的(测试)应用程序上下文中:
祝你好运!
一个使用Spring 4测试的解决方案,当您需要请求范围的bean但没有通过MockMVC
等提出任何请求时。
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(/* ... */) public class Tests { @Autowired private GenericApplicationContext context; @Before public void defineRequestScope() { context.getBeanFactory().registerScope( WebApplicationContext.SCOPE_REQUEST, new RequestScope()); RequestContextHolder.setRequestAttributes( new ServletRequestAttributes(new MockHttpServletRequest())); } // ...
使用Spring的测试请求 – Scoped Bean非常好地解释了如何使用Spring注册和创建自定义范围。
简而言之,正如Ido Cohn所解释的那样,将以下内容添加到文本上下文配置中就足够了:
而不是使用基于ThreadLocal的预定义SimpleThreadScope,它也很容易实现自定义,如文章中所述。
import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.Scope; public class CustomScope implements Scope { private final Map beanMap = new HashMap (); public Object get(String name, ObjectFactory> factory) { Object bean = beanMap.get(name); if (null == bean) { bean = factory.getObject(); beanMap.put(name, bean); } return bean; } public String getConversationId() { // not needed return null; } public void registerDestructionCallback(String arg0, Runnable arg1) { // not needed } public Object remove(String obj) { return beanMap.remove(obj); } public Object resolveContextualObject(String arg0) { // not needed return null; } }
MariuszS的解决方案有效,但我无法正确提交交易。
似乎新发布的3.2终于使测试请求/会话范围豆一等公民。 这里有几个博客了解更多细节。
Rossen Stoyanchev的Spring Framework 3.2 RC1:Spring MVC测试框架
Sam Brannen的Spring Framework 3.2 RC1:新的测试function
不阅读文档有时会让人发疯。 几乎。
如果您使用寿命较短的bean(例如请求范围),您很可能还需要更改延迟初始化默认值! 否则,WebAppContext将无法加载并告诉您有关缺少请求范围的信息,这当然是缺失的,因为上下文仍在加载!
spring的家伙们一定要把这个暗示放到他们的exception信息中……
如果您不想更改默认值,还有注释方式:在@Component等之后放置“@Lazy(true)”以使单例初始化延迟并避免过早地实例化请求范围的bean。
- 使用具有负边缘权重的Bellman-Ford追踪最长路径
- Java无法通过JDBC-ODBC从Access检索Unicode(立陶宛语)字母
- 打印java scriptlet变量,就好像它是一个JavaScript变量一样
- Spring 3.1 Environment不适用于用户属性文件
- 为什么hibernate给出了ConstraintException,就好像在已经存在的情况下尝试创建对象一样
- 添加到ArrayList Java
- 从命令选项卡应用程序切换器隐藏Java应用程序并在OSX中停靠
- Java Server – 使用POST发送Push到Google Firebase Cloud
- 您在Java中如何以及在何处定义自己的Exception层次结构?