Pergunta

Gostaria de fazer uso do escopo de requisição feijão em meu aplicativo. Eu uso JUnit4 para testar. Se eu tentar criar um em um teste como este:

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

Com a seguinte definição de 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>           

E eu recebo:

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'

Então, eu encontrei este blog que parecia útil: http://www.javathinking.com/2009 /06/no-scope-registered-for-scope-request_5.html

Mas eu notei que ele usa AbstractDependencyInjectionSpringContextTests que parece ser preterido na mola 3.0. Eu uso Spring 2.5 neste momento, mas pensei que não deve ser muito difícil para mudar esse método para usar AbstractJUnit4SpringContextTests como os documentos sugerem (ok os docs link para a versão 3.8, mas eu estou usando 4.4). Então eu mudar o teste para estender AbstractJUnit4SpringContextTests ... mesma mensagem. Mesmo problema. E agora o método prepareTestInstance () Eu quero a substituição não é definida. OK, talvez eu vou colocar essas chamadas registerScope em outro lugar ... Então eu li mais sobre TestExecutionListeners e acho que seria melhor desde que eu não quero ter que herdar a estrutura do pacote de primavera. então Mudei de teste para:

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

esperando que eu teria que criar um ouvinte costume, mas eu quando eu corri-lo. Funciona! Grande, mas por quê? Não vejo onde qualquer um dos ouvintes de ações está registrando pedido escopo ou escopo da sessão, e por que eles? não há nada para dizer que eu quero que, no entanto, isso pode não ser um teste para o código Spring MVC ...

Foi útil?

Solução

O teste passa, porque ele não está fazendo nada:)

Quando você omitir a anotação @TestExecutionListeners, Primavera registra 3 ouvintes padrão, incluindo um chamado DependencyInjectionTestExecutionListener. Este é o ouvinte responsável pela digitalização de sua classe de teste à procura de coisas para injetar, incluindo anotações @Resource. Este ouvinte tentou tObj injetar, e não consegue, por causa do escopo indefinido.

Quando você declara @TestExecutionListeners({}), você suprimir o registro da DependencyInjectionTestExecutionListener, e assim o teste nunca é tObj injetado em tudo, e porque o seu teste não é verificar a existência de tObj, ele passa.

Modifique o seu teste para que ele faz isso, e ele irá falhar:

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

Assim, com o seu @TestExecutionListeners vazio, o teste passa porque nada acontece .

Agora, sobre seu problema original. Se você quiser tentar registrar o escopo da solicitação com o seu contexto de teste, em seguida, ter um olhar para o código-fonte para WebApplicationContextUtils.registerWebApplicationScopes(), você vai encontrar a linha:

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

Você poderia tentar isso e ver como você vai, mas pode haver efeitos colaterais estranhos, porque você não está realmente quis fazer isso em um teste.

Em vez disso, eu recomendaria reformular o seu teste para que você não necessidade escopo de requisição feijão. Isso não deve ser difícil, o ciclo de vida do @Test não deve ser mais longo do que o ciclo de vida de um bean com escopo de solicitação, se você escrever testes de auto-contido. Lembre-se, não há nenhuma necessidade para testar o mecanismo de definição do âmbito, é parte da Primavera e você pode assumir ele funciona.

Outras dicas

Solução para Primavera 3.2 ou mais recente

Spring começando com a versão 3.2 fornece suporte para sessão / escopo de requisição feijão para testes de integração .

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

Leia mais: Request e escopo de sessão beans


Solução para Primavera antes de 3.2 com ouvinte

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

Solução para Primavera antes de 3.2 com o costume escopos

@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

}

ou com XML de configuração

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>

O código-fonte

O código fonte para todas as soluções apresentadas:

Eu tentei várias soluções, incluindo @ solução de Marius com o "WebContextTestExecutionListener", mas não funcionou para mim, como este código carregado o contexto do aplicativo antes de criar o escopo da solicitação.

A resposta que me ajudou no final, não é nova, mas é bom: http://tarunsapra.wordpress.com/2011/06 / 28 / junit-spring-sessão-e-pedido de escopo em grão /

Eu simplesmente adicionado o seguinte trecho ao meu (teste) contexto de aplicação:

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

Boa sorte!

Uma solução, testada com Spring 4, para quando você precisar de feijão com escopo de solicitação, mas não estão fazendo quaisquer solicitações 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()));
    }

    // ...

Esta é ainda uma questão em aberto:

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

Eu era capaz de chegar a este trabalho (principalmente) através da definição de um carregador de contexto personalizado, conforme descrito no

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

teste Beans com escopo de solicitação com Primavera explica muito bem como registrar e criar um escopo personalizado com a Primavera.

Em poucas palavras, como explicado Ido Cohn, é suficiente para adicionar o seguinte para a configuração de contexto do texto:

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

Em vez de usar o SimpleThreadScope predefinidos, com base em ThreadLocal, também é fácil de implementar um personalizado, conforme explicado no artigo.

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

solução funciona 'MariuszS, só que eu não poderia começar a transação confirmada corretamente.

Parece que os recém-lançado 3.2 finalmente fez testes solicitação / sessão feijão escopo cidadãos de primeira classe. Aqui está um par de blogs para mais detalhes.

O Rossen Stoyanchev Spring Framework 3.2 RC1: Spring MVC Test Framework

O Sam Brannen Primavera Framework 3.2 RC1: New Testing Características

não ler os docs, por vezes, leva um louco. Quase.

Se você estiver usando grãos mais curtos duração (pedido âmbito, por exemplo), você provavelmente também precisa mudar o seu padrão de inicialização preguiçoso! Caso contrário, o WebAppContext falhará para carregar e dizer-lhe algo sobre a falta de escopo de solicitação, que é, naturalmente, falta, porque o contexto é ainda o carregamento!

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

Os caras Primavera deve definitivamente colocar essa dica em sua mensagem de exceção ...

Se você não quiser alterar o padrão, há também a maneira anotação: colocar "@Lazy (true)" depois @Component etc, para fazer singletons inicializar preguiçoso e evitar instanciar feijão com escopo de solicitação muito cedo <. / p>

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top