Domanda

Vorrei utilizzare i bean con ambito richiesta nella mia app.Utilizzo JUnit4 per i test.Se provo a crearne uno in un test come questo:

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

Con la seguente definizione di 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 ottengo:

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'

Così ho trovato questo blog che mi è sembrato utile:http://www.javathinking.com/2009/06/no-scope-registered-for-scope-request_5.html

Ma ho notato che usa AbstractDependencyInjectionSpringContextTests che sembra essere deprecato nella primavera 3.0.Uso Spring 2.5 in questo momento, ma ho pensato che non dovrebbe essere troppo difficile cambiare questo metodo per utilizzare AbstractJUnit4SpringContextStest come suggeriscono i documenti (OK il collegamento dei documenti alla versione 3.8 ma sto usando 4.4).Quindi cambio il test per estendere AbstractJUnit4SpringContextTests ...stesso messaggio.Stesso problema.E ora il metodo PreparateStinstance () che voglio sovrascrivere non è definito.OK, forse metterò quelle chiamate RegisterScope da qualche altra parte...Quindi ho letto di più TestExecutionListeners e penso che sarebbe meglio dal momento che non voglio dover ereditare la struttura del pacchetto primaverile.Quindi ho cambiato il mio test in:

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

aspettandomi avrei dovuto creare un ascoltatore personalizzato, ma io quando l'ho eseguito.Funziona!Ottimo, ma perché?Non vedo dove uno degli ascoltatori di stock registra l'ambito della richiesta o l'ambito della sessione e perché dovrebbero?non c'è niente da dire, lo voglio ancora, questo potrebbe non essere un codice MVC Test for Spring...

È stato utile?

Soluzione

Il test viene superato, perché non sta facendo nulla:)

Quando si omette l'annotazione @TestExecutionListeners, Primavera registra 3 ascoltatori predefinite, tra cui uno chiamato DependencyInjectionTestExecutionListener. Questo è l'ascoltatore responsabile per la scansione del classe di test alla ricerca di cose per iniettare, tra cui le annotazioni @Resource. Questo ascoltatore ha cercato di iniettare tObj, e fallisce, a causa della portata indefinito.

Quando si dichiara @TestExecutionListeners({}), si elimina la registrazione della DependencyInjectionTestExecutionListener, e quindi il test non viene mai tObj iniettato a tutti, e perché il test non è il controllo per l'esistenza di tObj, passa.

Modifica la prova in modo che lo fa, e fallirà:

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

Quindi, con il vostro @TestExecutionListeners vuoto, il test viene superato, perché non succede nulla .

Ora, al vostro problema originale. Se volete provare la registrazione della portata richiesta alla tua contesto di prova, poi date un'occhiata al codice sorgente per WebApplicationContextUtils.registerWebApplicationScopes(), troverete la linea:

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

Si potrebbe provare questo, e vedere come si va, ma ci potrebbe essere dispari effetti collaterali, perché non stai veramente destinata a fare questo in un test.

Al contrario, mi sento di raccomandare riformulare il test in modo che non si necessità richiesta ambito fagioli. Questo non dovrebbe essere difficile, il ciclo di vita del @Test non dovrebbe essere più lungo del ciclo di vita di un fagiolo di richiesta con ambito, se si scrive i test self-contained. Ricordate, non c'è bisogno di testare il meccanismo di scoping, è parte di primavera e si può assumere che funziona.

Altri suggerimenti

Soluzione per la primavera 3.2 o più recenti

Primavera a partire dalla versione 3.2 fornisce il supporto per la sessione / richiesta di scope di fagioli per test di integrazione .

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

Per saperne di più: noreferrer di richiesta e di scope di sessione fagioli


Soluzione per la primavera prima di 3.2 con ascoltatore

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

Soluzione per la primavera prima di 3.2 con ambiti personalizzati

@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

}

o con configurazione 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>

Il codice sorgente

Il codice sorgente per tutte le soluzioni presentate:

Ho provato diverse soluzioni, tra cui @ soluzione di Marius con la "WebContextTestExecutionListener", ma non ha funzionato per me, in quanto questo codice caricato il contesto dell'applicazione prima di creare il campo di applicazione richiesta.

La risposta che mi ha aiutato alla fine non è nuova, ma è buona: http://tarunsapra.wordpress.com/2011/06 / 28 / JUnit-spring-session-e-richiesta-portata-fagioli /

ho semplicemente aggiunto il seguente frammento alla mia (test) contesto di applicazione:

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

In bocca al lupo!

Una soluzione, testata con molla 4, per cui si richiedono i fagioli di richiesta con ambito, ma non stanno facendo alcuna richiesta tramite MockMVC, ecc.

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

    // ...

Questo è ancora una questione aperta:

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

Sono stato in grado di ottenere questo al lavoro (per lo più) definendo un caricatore contesto personalizzato come delineato nella

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

test Fagioli richiesta con ambito con molla spiega molto bene come registrarsi e creare un ambito personalizzato con la Primavera.

In poche parole, come spiega Ido Cohn, è sufficiente aggiungere il seguente alla configurazione contesto testo:

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

Invece di usare il predefinito SimpleThreadScope, sulla base di ThreadLocal, è anche facile da implementare uno personalizzato, come spiegato in questo articolo.

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

La soluzione di MariuszS funziona, tranne per il fatto che non sono riuscito a eseguire correttamente la transazione.

Sembra che il nuovo rilascio 3.2 abbia finalmente reso cittadini di prima classe i bean con ambito richiesta/sessione di test.Ecco un paio di blog per maggiori dettagli.

Quello di Rossen Stoyanchev Spring Framework 3.2 RC1:Framework di test MVC di primavera

Quello di Sam Brannen Spring Framework 3.2 RC1:Nuove funzionalità di test

non leggere la documentazione a volte spinge pazzo. Quasi.

Se si utilizza i fagioli più breve durata (richiesta portata ad esempio), molto probabilmente anche necessario modificare il valore predefinito init pigro! In caso contrario, il WebAppContext non riuscirà a caricare e dirvi qualcosa circa la portata richiesta mancante, che è ovviamente mancante, perché il contesto è ancora carico!

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

I ragazzi di primavera dovrebbe sicuramente messo quel pizzico nel loro messaggio di eccezione ...

Se non si desidera modificare il valore predefinito, v'è anche il modo di annotazione: mettere "@Lazy (vero)" dopo @Component ecc per rendere single inizializzare pigro ed evitare istanziare fagioli richiesta con ambito troppo presto <. / p>

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top