アプリでリクエストスコープ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()メソッドが定義されていません。 OK、多分私はそれらのregisterScope呼び出しを別の場所に置きます...それで TestExecutionListeners についてもっと読んで、春のパッケージ構造を継承する必要がないのでそれがより良いと思います。だから私は私のテストを次のように変更しました:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
@TestExecutionListeners({})
public class TestScopedBeans {
カスタムリスナーを作成する必要があると思っていましたが、実行したときです。できます!素晴らしいですが、なぜですか?ストックリスナーのどこがリクエストスコープまたはセッションスコープを登録しているのかわかりません。なぜですか。まだ言いたいことは何もありませんが、これはTest for Spring MVCコードではないかもしれません...
それは何もしていないのでテストは合格です:)
_@TestExecutionListeners
_アノテーションを省略すると、SpringはDependencyInjectionTestExecutionListener
と呼ばれるリスナーを含む3つのデフォルトリスナーを登録します。これは、_@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を要求するneedしないように、テストを言い換えることをお勧めします。自己完結型のテストを作成する場合、これは難しいことではありません。_@Test
_のライフサイクルは、リクエストスコープのBeanのライフサイクルより長くなることはありません。スコーピングメカニズムをテストする必要はありません。これはSpringの一部であり、正常に機能すると想定できます。
Springバージョン3.2以降 統合テスト用のセッション/リクエストスコープ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));
}
}
続きを読む: 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>
提示されたすべてのソリューションのソースコード:
「WebContextTestExecutionListener」を使用した@Mariusのソリューションを含むいくつかのソリューションを試しましたが、このコードはリクエストスコープを作成する前にアプリケーションコンテキストをロードしたため、機能しませんでした。
最後に私を助けた答えは新しいものではありませんが、それは良いことです: http://tarunsapra.wordpress.com/2011/06/28/junit-spring-session-and-request-scope-豆/
(テスト)アプリケーションコンテキストに次のスニペットを追加しただけです。
<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>
幸運を!
要求スコープのBeanが必要であるがMockMVC
などを介して要求を行わない場合の、Spring 4でテストされたソリューション。
@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でリクエストスコープBeanをテストする は、Springでカスタムスコープを登録および作成する方法を非常によく説明しています。
一言で言えば、イドコーンが説明したように、テキストコンテキスト構成に以下を追加するだけで十分です。
<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;
}
}
これはまだ未解決の問題です:
https://jira.springsource.org/browse/SPR-4588
で概説されているようにカスタムコンテキストローダーを定義することでこれを(ほとんど)動作させることができました
MariuszSのソリューションは機能しますが、トランザクションを適切にコミットできませんでした。
新しくリリースされた3.2がようやくテスト要求/セッションスコープBeanファーストクラスの市民を作ったようです。詳細については、いくつかのブログをご覧ください。
Rossen Stoyanchevの Spring Framework 3.2 RC1:Spring MVC Test Framework
Sam Brannenの Spring Framework 3.2 RC1:新しいテスト機能
ドキュメントを読まないと、時々気が狂います。ほとんど。
存続期間の短いBean(リクエストスコープなど)を使用している場合は、遅延initのデフォルトを変更する必要もあるでしょう。そうでない場合、WebAppContextはロードに失敗し、コンテキストがまだロードされているため、欠落しているリクエストスコープに関する情報を提供します。
春の連中は間違いなくそのヒントを例外メッセージに入れるべきだ...
デフォルトを変更したくない場合は、アノテーションの方法もあります。@ Componentなどの後に「@Lazy(true)」を配置して、シングルトンでレイジーを初期化し、リクエストスコープのBeanが早くインスタンス化されないようにします。