我想在我的应用中使用请求范围的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定义:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="java.lang.Object" id="tObj" scope="request" />
</beans>
我得到了:
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'
<...SNIP...>
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的内容,并认为这会更好,因为我不想继承spring包结构。所以 我将测试改为:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
@TestExecutionListeners({})
public class TestScopedBeans {
期待我必须创建自定义侦听器,但是当我运行它时。有用!太好了,但为什么呢?我没有看到任何股票听众的位置 正在注册请求范围或会话范围,为什么会这样?没有什么可说的,我想要的,这可能不是Spring MVC代码的测试...
答案 0 :(得分:51)
Spring从版本3.2 provides support for session/request scoped beans for integration testing开始。
@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));
}
}
了解详情:Request and Session Scoped Beans
@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());
}
}
}
@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<String, Object> scopes = new HashMap<String, Object>();
scopes.put(WebApplicationContext.SCOPE_REQUEST,
new SimpleThreadScope());
scopes.put(WebApplicationContext.SCOPE_SESSION,
new SimpleThreadScope());
scopeConfigurer.setScopes(scopes);
return scopeConfigurer
}
或使用xml配置
test-config.xml
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
<map>
<entry key="session">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
所有呈现解决方案的源代码:
答案 1 :(得分:8)
测试通过,因为它没有做任何事情:)
当您省略@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的一部分,你可以认为它有效。
答案 2 :(得分:8)
我尝试了几种解决方案,包括@ Marius的“WebContextTestExecutionListener”解决方案,但它对我不起作用,因为此代码在创建请求范围之前加载了应用程序上下文。
最终帮助我的答案不是新的,但它很好: http://tarunsapra.wordpress.com/2011/06/28/junit-spring-session-and-request-scope-beans/
我只是将以下代码段添加到我的(测试)应用程序上下文中:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
祝你好运!
答案 3 :(得分:4)
使用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()));
}
// ...
答案 4 :(得分:2)
答案 5 :(得分:2)
Test Request-Scoped Beans with Spring非常好地解释了如何使用Spring注册和创建自定义作用域。
简而言之,正如Ido Cohn所解释的那样,将以下内容添加到文本上下文配置中就足够了:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
而不是使用基于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<String , Object> beanMap = new HashMap<String , Object>();
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;
}
}
答案 6 :(得分:1)
MariuszS'解决方案有效,但我无法正确提交交易。
似乎新发布的3.2终于使测试请求/会话范围豆类成为一等公民。这里有几个博客了解更多细节。
Rossen Stoyanchev的Spring Framework 3.2 RC1: Spring MVC Test Framework
Sam Brannen的Spring Framework 3.2 RC1: New Testing Features
答案 7 :(得分:0)
不阅读文档有时会让人发疯。几乎。
如果您使用寿命较短的bean(例如请求范围),您很可能还需要更改延迟初始化默认值!否则,WebAppContext将无法加载并告诉您有关缺少请求范围的信息,这当然是缺失的,因为上下文仍在加载!
春天的家伙们一定要把这个提示放到他们的异常信息中......
如果您不想更改默认值,还有注释方式:在@Component等之后放置“@Lazy(true)”以使单例初始化延迟并避免过早实例化请求范围的bean。 / p>