복붙노트

[SPRING] 봄철 테스트에서 범위가 지정된 콩을 요청하십시오.

SPRING

봄철 테스트에서 범위가 지정된 콩을 요청하십시오.

내 응용 프로그램에서 요청 범위 콩을 사용하고 싶습니다. 테스트를 위해 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

하지만 Spring 3.0에서 사용되지 않는 것으로 보이는 AbstractDependencyInjectionSpringContextTests를 사용하는 것으로 나타났습니다. 나는이 시점에서 Spring 2.5를 사용하지만, AbstractJUnit4SpringContextTests를 사용하기 위해이 메소드를 전환하는 것이 너무 어렵지 않아야한다고 생각했습니다. docs가 제안한 것처럼 (물론 docs는 3.8 버전으로 링크하지만 4.4를 사용하고 있습니다). 그래서 나는 AbstractJUnit4SpringContextTests ... 같은 메시지를 확장하기위한 테스트. 같은 문제. 그리고 지금 내가 원하는 prepareTestInstance () 메소드 재정의가 정의되지 않았습니다. 좋습니다, 아마도 registerScope 호출을 다른 곳에 넣을 것입니다 ... 그래서 저는 TestExecutionListeners에 대해 더 많이 읽고 스프링 패키지 구조를 상속 받아야하기 때문에 좋을 것이라고 생각합니다. 그래서 나는 나의 시험을 다음과 같이 바꿨다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
@TestExecutionListeners({})
public class TestScopedBeans {

커스텀 리스너를 만들어야하지만, 내가 그것을 실행했을 때 나는 기대했다. 그것은 작동합니다! 좋아, 근데 왜? 재고 리스너가 어디 있는지 모르겠다. 요청 범위 또는 세션 범위를 등록하는 이유는 무엇입니까? 아직 Spring MVC 코드 테스트가 아닐 수도 있습니다.

해결법

  1. ==============================

    1.그것은 아무것도하지 않기 때문에 테스트 통과 :)

    그것은 아무것도하지 않기 때문에 테스트 통과 :)

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

    당신은 그것을 시도 할 수 있고, 당신이 어떻게 가는지 볼 수는 있지만, 테스트에서 이것을하기위한 것이 아니기 때문에 이상한 부작용이있을 수 있습니다.

    대신 요청 범위가 필요한 콩을 필요로하지 않도록 테스트를 수정하는 것이 좋습니다. 이것은 어렵지 않아야합니다. @Test의 라이프 사이클은 자체 검사를 작성하는 경우 요청 범위가 지정된 bean의 수명보다 길어서는 안됩니다. 범위 지정 메커니즘을 테스트 할 필요가 없다는 것을 기억하십시오. 이는 스프링의 일부이며 작동한다고 가정 할 수 있습니다.

  2. ==============================

    2.버전 3.2부터는 통합 테스트를 위해 세션 / 요청 범위 Bean을 지원합니다.

    버전 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 {
        ...
    }
    
    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 {
    
    ...
    
    }
    
    @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 구성

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

    제시된 모든 솔루션의 소스 코드 :

  3. ==============================

    3."WebContextTestExecutionListener"를 사용하여 @ Marius의 솔루션을 포함한 여러 솔루션을 시도했지만이 코드가 요청 범위를 만들기 전에 응용 프로그램 컨텍스트를로드했기 때문에 나에게 도움이되지 않았습니다.

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

    행운을 빕니다!

  4. ==============================

    4.요청 범위 Bean이 필요하지만 MockMVC를 통해 요청을하지 않는 경우를 대비하여 Spring 4로 테스트 한 솔루션

    요청 범위 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()));
        }
    
        // ...
    
  5. ==============================

    5.이것은 여전히 ​​열려있는 문제입니다.

    이것은 여전히 ​​열려있는 문제입니다.

    https://jira.springsource.org/browse/SPR-4588

    나는이 기능을 (주로) 다음에 설명 된대로 사용자 정의 컨텍스트 로더를 정의하여 작동시킬 수있었습니다.

    http://forum.springsource.org/showthread.php?p=286280

  6. ==============================

    6.Test Request-Scoped Beans with Spring은 Spring을 사용하여 사용자 정의 범위를 등록하고 작성하는 방법을 잘 설명합니다.

    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를 사용하는 대신이 기사에서 설명한대로 Custom을 구현하는 것도 쉽습니다.

    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;
        }
    }
    
  7. ==============================

    7.MariuszS '솔루션은 제대로 작동하는 트랜잭션을 얻을 수 없다는 점을 제외하고는 작동합니다.

    MariuszS '솔루션은 제대로 작동하는 트랜잭션을 얻을 수 없다는 점을 제외하고는 작동합니다.

    새로 출시 된 3.2가 마침내 요청 / 세션 범위의 콩을 일류 시민으로 테스트 한 것으로 보입니다. 자세한 내용은 두 가지 블로그가 있습니다.

    Rossen Stoyanchev의 Spring Framework 3.2 RC1 : Spring MVC 테스트 프레임 워크

    Sam Brannen의 Spring Framework 3.2 RC1 : 새로운 테스트 기능

  8. ==============================

    8.문서를 읽지 않으면 가끔은 미친 듯이 운전됩니다. 거의.

    문서를 읽지 않으면 가끔은 미친 듯이 운전됩니다. 거의.

    수명이 짧은 bean (예를 들어 요청 범위)을 사용한다면, 당신은 lazy init default를 변경할 필요가있을 것이다. 그렇지 않으면 컨텍스트가 여전히로드되어 있기 때문에 WebAppContext가로드되지 않고 누락 된 요청 범위에 대해 알려주지 않습니다.

    http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-lazy-init

    Spring 사람들은 예외 메시지에 그 힌트를 확실히 넣어야합니다 ...

    기본값을 변경하고 싶지 않다면 주석 방법도 있습니다 : @Component 등 뒤에 "@Lazy (true)"를 넣으면 싱글 톤을 게으르게 초기화하고 요청 범위가 너무 큰 콩을 너무 일찍 인스턴스화하지 않게됩니다.

  9. from https://stackoverflow.com/questions/2411343/request-scoped-beans-in-spring-testing by cc-by-sa and MIT license