request scoped beans in spring testing

后端 未结 8 825
自闭症患者
自闭症患者 2020-11-30 00:54

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(SpringJUnit4Clas         


        
相关标签:
8条回答
  • 2020-11-30 01:07

    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

    0 讨论(0)
  • 2020-11-30 01:13

    A solution, tested with Spring 4, for when you require request-scoped beans but aren't making any requests via MockMVC, etc.

    @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()));
        }
    
        // ...
    
    0 讨论(0)
  • 2020-11-30 01:16

    Solution for Spring 3.2 or newer

    Spring starting with version 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));
        }
    }
    

    Read more: Request and Session Scoped Beans


    Solution for Spring before 3.2 with listener

    @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());
            }
        }
    }
    

    Solution for Spring before 3.2 with custom scopes

    @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
    
    }
    

    or with xml configuration

    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>
    

    Source code

    Source code for all presented solutions:

    • https://github.com/mariuszs/spring-test-web
    0 讨论(0)
  • 2020-11-30 01:17

    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:

    <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>
    

    Good luck!

    0 讨论(0)
  • 2020-11-30 01:18

    The test passes because it isn't doing anything :)

    When you omit the @TestExecutionListeners annotation, Spring registers 3 default listeners, including one called DependencyInjectionTestExecutionListener. This is the listener responsible for scanning your test class looking for things to inject, including @Resource annotations. This listener tried to inject tObj, and fails, because of the undefined scope.

    When you declare @TestExecutionListeners({}), you suppress the registration of the DependencyInjectionTestExecutionListener, and so the test never gets tObj injected at all, and because your test is not checking for the existence of tObj, it passes.

    Modify your test so that it does this, and it will fail:

    @Test
    public void testBean() {
        assertNotNull("tObj is null", tObj);
    }
    

    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:

    beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
    

    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.

    0 讨论(0)
  • 2020-11-30 01:20

    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.

    0 讨论(0)
提交回复
热议问题