在春季测试中请求范围豆

har*_*are 28 java junit spring spring-mvc spring-test

我想在我的应用程序中使用请求范围的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);
    }
Run Code Online (Sandbox Code Playgroud)

使用以下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>           
Run Code Online (Sandbox Code Playgroud)

我得到:

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'
Run Code Online (Sandbox Code Playgroud)

所以我发现这个博客看起来很有帮助: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 {
Run Code Online (Sandbox Code Playgroud)

期待我必须创建一个自定义监听器,但是当我运行它时.有用!太好了,但为什么呢?我没有看到任何股票监听者在哪里注册请求范围或会话范围,为什么会这样?没有什么可说的,我想要的,这可能不是Spring MVC代码的测试......

Mar*_*szS 54

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

阅读更多:请求和会话范围豆


带有监听器的3.2之前的Spring解决方案

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class)
@TestExecutionListeners({WebContextTestExecutionListener.class,
        DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class})
public class SampleTest {
    ...
}
Run Code Online (Sandbox Code Playgroud)

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());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

带有自定义范围的3.2之前的Spring解决方案

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfig.class, locations = "test-config.xml")
public class SampleTest {

...

}
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

或者使用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>
Run Code Online (Sandbox Code Playgroud)

源代码

所有呈现解决方案的源代码:


Ido*_*ohn 9

我尝试了几种解决方案,包括带有"WebContextTestExecutionListener"的@ Marius解决方案,但它对我不起作用,因为此代码在创建请求范围之前加载了应用程序上下文.

最终帮助我的答案不是新的,但它很好: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>
Run Code Online (Sandbox Code Playgroud)

祝好运!


ska*_*man 8

测试通过,因为它没有做任何事情:)

省略@TestExecutionListeners注释时,Spring会注册3个默认侦听器,包括一个名为DependencyInjectionTestExecutionListener.这是监听器,负责扫描您的测试类,寻找要注入的内容,包括@Resource注释.tObj由于未定义的范围,此侦听器尝试注入并失败.

当你声明时@TestExecutionListeners({}),你禁止注册DependencyInjectionTestExecutionListener,因此测试永远不会被tObj注入,并且因为你的测试没有检查是否存在tObj,它就会通过.

修改您的测试,以便它执行此操作,它将失败:

@Test
public void testBean() {
    assertNotNull("tObj is null", tObj);
}
Run Code Online (Sandbox Code Playgroud)

因此@TestExecutionListeners,如果没有任何反应,则测试通过.

现在,关于你原来的问题.如果您想尝试使用测试上下文注册请求范围,那么请查看源代码WebApplicationContextUtils.registerWebApplicationScopes(),您将找到以下行:

beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
Run Code Online (Sandbox Code Playgroud)

你可以尝试一下,看看你怎么做,但可能会有奇怪的副作用,因为你并不是真的想在测试中做到这一点.

相反,我建议改写您的测试,以便您不需要请求范围的bean.这应该不难,@Test如果你编写自包含的测试,那么生命周期不应该长于请求范围的bean的生命周期.请记住,没有必要测试作用域机制,它是Spring的一部分,你可以认为它有效.

  • 谢谢,但是如何将你的测试"改写"为_need_ request scoped beans?假设我正在测试具有@Autowired Provider <FooDao> fooDao的FooController.我不是在嘲笑fooDao,因为这是集成测试,而不是单元(单元测试根本不需要Spring上下文),我真的需要真正的FooDao.你如何注入请求范围的fooDao? (2认同)

Sto*_*ica 5

一种解决方案,已在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()));
    }

    // ...
Run Code Online (Sandbox Code Playgroud)