在春季测试中请求范围内的bean
我想在我的应用程序中使用请求范围的 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 代码的测试......
I would like to make use of request scoped beans in my app. I use JUnit4 for testing. If I try to create one in a test like this:
@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);
}
With the following bean definition:
<?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>
And I get:
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'
So I found this blog that seemed helpful:
http://www.javathinking.com/2009/06/no-scope-registered-for-scope-request_5.html
But I noticed he uses AbstractDependencyInjectionSpringContextTests which seems to be deprecated in Spring 3.0.
I use Spring 2.5 at this time, but thought it shouldn't be too hard to switch this method to use AbstractJUnit4SpringContextTests
as the docs suggest (ok the docs link to the 3.8 version but I'm using 4.4). So I change the
test to extend AbstractJUnit4SpringContextTests... same message. Same problem. And now the prepareTestInstance() method I want
to override is not defined. OK, maybe I'll put those registerScope calls somewhere else... So I read more about TestExecutionListeners and think that would be better since I don't want to have to inherit the spring package structure. So
I changed my Test to:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
@TestExecutionListeners({})
public class TestScopedBeans {
expecting I would have to create a custom listener but I when I ran it. It works! Great, but why? I don't see where any of the stock listeners
are registering request scope or session scope, and why would they? there's nothing to say I want that yet, this might not be a Test for Spring MVC code...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
Spring 3.2 或更高版本的解决方案
从版本 3.2 开始,Spring 提供对会话/请求作用域 bean 的支持集成测试。
了解更多:请求和会话作用域 Bean
Spring 3.2 之前的解决方案,带有侦听器
WebContextTestExecutionListener.java
Spring 3.2 之前的解决方案,带有自定义范围
TestConfig.java
或使用 xml 配置
test-config.xml
源代码
所有提供的解决方案的源代码:
Solution for Spring 3.2 or newer
Spring starting with version 3.2 provides support for session/request scoped beans for integration testing.
Read more: Request and Session Scoped Beans
Solution for Spring before 3.2 with listener
WebContextTestExecutionListener.java
Solution for Spring before 3.2 with custom scopes
TestConfig.java
or with xml configuration
test-config.xml
Source code
Source code for all presented solutions:
我尝试了几种解决方案,包括@Marius 的“WebContextTestExecutionListener”解决方案,但它对我不起作用,因为此代码在创建请求范围之前加载了应用程序上下文。
最终对我有帮助的答案不是新答案,但很好:
http://tarunsapra.wordpress.com/2011/06 /28/junit-spring-session-and-request-scope-beans/
我只是将以下代码片段添加到我的(测试)应用程序上下文中:
祝你好运!
I've tried several solutions, including @Marius's solution with the "WebContextTestExecutionListener", but it didn't work for me, as this code loaded the application context before creating the request scope.
The answer that helped me in the end is not a new one, but it's good:
http://tarunsapra.wordpress.com/2011/06/28/junit-spring-session-and-request-scope-beans/
I simply added the following snippet to my (test) application context:
Good luck!
一个使用 Spring 4 进行测试的解决方案,适用于当您需要请求范围的 bean 但不通过
MockMVC
等发出任何请求时。A solution, tested with Spring 4, for when you require request-scoped beans but aren't making any requests via
MockMVC
, etc.测试通过是因为它没有执行任何操作:)
当您省略
@TestExecutionListeners
注释时,Spring 会注册 3 个默认侦听器,其中包括一个名为DependencyInjectionTestExecutionListener
的侦听器。这是负责扫描测试类以查找要注入的内容的侦听器,包括 @Resource 注释。此侦听器尝试注入tObj
,但由于未定义范围而失败。当您声明
@TestExecutionListeners({})
时,您会抑制DependencyInjectionTestExecutionListener
的注册,因此测试根本不会被注入tObj
,因为您的测试没有检查 tObj 是否存在,所以它通过了。修改您的测试,使其执行此操作,但它将失败:
因此,使用空的
@TestExecutionListeners
时,测试会通过,因为什么也没有发生。现在,回到你原来的问题。如果您想尝试在测试上下文中注册请求范围,请查看
WebApplicationContextUtils.registerWebApplicationScopes()
的源代码,您会发现以下行:您可以尝试一下,然后查看你怎么做,但可能会有奇怪的副作用,因为你并不是真的打算在测试中这样做。
相反,我建议重新措辞您的测试,以便您不需要请求作用域 bean。这应该不难,如果您编写独立的测试,则
@Test
的生命周期不应比请求范围 bean 的生命周期长。请记住,无需测试作用域机制,它是 Spring 的一部分,您可以假设它有效。The test passes because it isn't doing anything :)
When you omit the
@TestExecutionListeners
annotation, Spring registers 3 default listeners, including one calledDependencyInjectionTestExecutionListener
. This is the listener responsible for scanning your test class looking for things to inject, including@Resource
annotations. This listener tried to injecttObj
, and fails, because of the undefined scope.When you declare
@TestExecutionListeners({})
, you suppress the registration of theDependencyInjectionTestExecutionListener
, and so the test never getstObj
injected at all, and because your test is not checking for the existence oftObj
, it passes.Modify your test so that it does this, and it will fail:
So with your empty
@TestExecutionListeners
, the test passes because nothing happens.Now, on to your original problem. If you want to try registering the request scope with your test context, then have a look at the source code for
WebApplicationContextUtils.registerWebApplicationScopes()
, you'll find the line:You could try that, and see how you go, but there might be odd side-effects, because you're not really meant to do this in a test.
Instead, I would recommend rephrasing your test so that you don't need request scoped beans. This shouldn't be difficult, the lifecycle of the
@Test
shouldn't be any longer than the lifecycle of a request-scoped bean, if you write self-contained tests. Remember, there's no need to test the scoping mechanism, it's part of Spring and you can assume it works.这仍然是一个悬而未决的问题:
https://jira.springsource.org/browse/SPR-4588< /a>
我能够通过定义自定义上下文加载器(主要)来使其工作,如
http://forum.springsource.org/showthread.php?p=286280
This is still an open issue:
https://jira.springsource.org/browse/SPR-4588
I was able to get this to work (mostly) by defining a custom context loader as outlined in
http://forum.springsource.org/showthread.php?p=286280
使用 Spring 测试请求范围的 Bean 很好地解释了如何使用 Spring 注册并创建自定义范围。
简而言之,正如 Ido Cohn 所解释的那样,将以下内容添加到文本上下文配置中就足够了:
不用使用预定义的 SimpleThreadScope,基于 ThreadLocal,也可以轻松实现自定义线程,如文章中所述。
Test Request-Scoped Beans with Spring explains very well how to register and create a custom scope with Spring.
In a nutshell, as Ido Cohn explained, it's enough to add the following to the text context configuration:
Instead of using the predefined SimpleThreadScope, based on ThreadLocal, it's also easy to implement a Custom one, as explained in the article.
MariuszS 的解决方案有效,但我无法正确提交事务。
看来新发布的 3.2 终于使测试请求/会话作用域的 beans 成为一等公民。这里有几个博客以了解更多详细信息。
Rossen Stoyanchev 的 Spring 框架3.2 RC1:Spring MVC 测试框架
Sam Brannen 的 Spring Framework 3.2 RC1:新测试功能
MariuszS' solution works, except I couldn't get the transaction committed properly.
It seems the newly released 3.2 has finally made testing request/session scoped beans first class citizens. Here's a couple of blogs for more details.
Rossen Stoyanchev's Spring Framework 3.2 RC1: Spring MVC Test Framework
Sam Brannen's Spring Framework 3.2 RC1: New Testing Features
不阅读文档有时会让人发疯。几乎。
如果您使用寿命较短的 bean(例如请求范围),您很可能还需要更改惰性初始化默认值!否则,WebAppContext 将无法加载并告诉您有关缺少请求范围的信息,这当然是缺少的,因为上下文仍在加载!
http ://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-lazy-init
Spring 的家伙绝对应该把这个提示放入他们的异常消息...
如果您不想更改默认值,还有一种注释方式:在@Component等之后放置“@Lazy(true)”,以使单例初始化延迟并避免过早实例化请求范围的bean 。
NOT reading the docs sometimes drives one crazy. Almost.
If you are using shorter-lived beans (request scope for example), you most likely also need to change your lazy init default! Otherwise the WebAppContext will fail to load and tell you something about missing request scope, which is of course missing, because the context is still loading!
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-lazy-init
The Spring guys should definitely put that hint into their exception message...
If you don't want to change the default, there is also the annotation way: put "@Lazy(true)" after @Component etc. to make singletons initialize lazy and avoid instantiating request-scoped beans too early.