Frage

Ich möchte Verwendung Antrag stellen scoped Bohnen in meiner app. Ich benutze JUnit4 zum Testen. Wenn ich versuche, eine in einem Test wie folgt zu erstellen:

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

Mit der folgenden Bohne Definition:

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

Und ich:

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'

So fand ich diesen Blog, die hilfreich schien: http://www.javathinking.com/2009 /06/no-scope-registered-for-scope-request_5.html

Aber ich bemerkte, dass er verwendet AbstractDependencyInjectionSpringContextTests die im Frühjahr 3.0 veraltet zu sein scheint. Ich verwende Spring 2.5 zu diesem Zeitpunkt, aber dachte, es sollte nicht allzu schwer sein, diese Methode zu wechseln AbstractJUnit4SpringContextTests zu verwenden wie die Docs vorschlagen (ok verknüpft die Dokumentation zur 3.8-Version, aber ich bin mit 4.4). So ändere ich die Test AbstractJUnit4SpringContextTests ... gleiche Botschaft zu erweitern. Gleiches Problem. Und nun die prepareTestInstance () -Methode Ich möchte außer Kraft setzen nicht definiert ist. OK, vielleicht werde ich diejenigen setzen registerScope woanders ruft ... Also las ich mehr über

War es hilfreich?

Lösung

Der Test besteht, weil es nichts tut:)

Wenn Sie die @TestExecutionListeners Anmerkung weglassen, Frühling Register 3 default Zuhörer, darunter eine namens DependencyInjectionTestExecutionListener. Dies ist der Hörer verantwortlich für Ihre Testklasse Scannen der Suche nach Dingen zu injizieren, einschließlich @Resource Anmerkungen. Dieser Hörer versucht tObj zu injizieren, und nicht wegen der undefinierten Umfang.

Wenn Sie @TestExecutionListeners({}) erklären, unterdrücken Sie die Registrierung des DependencyInjectionTestExecutionListener, und so der Test überhaupt nicht injiziert tObj wird, und weil Ihr Test ist nicht für die Existenz von tObj Überprüfung, es geht.

ändern Sie bitte Ihren Test, so dass es dies tut, und es wird fehlschlagen:

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

So mit leeren @TestExecutionListeners, der Test bestanden wird, weil nichts passiert .

Nun zu Ihrem ursprünglichen Problem. Wenn Sie den Antrag Umfang mit Ihrem Test Kontext Registrierung ausprobieren möchten, dann haben Sie einen Blick auf den Quellcode für WebApplicationContextUtils.registerWebApplicationScopes(), werden Sie die Zeile finden:

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

Sie könnten versuchen, dass, und sehen Sie, wie Sie gehen, aber es könnte ungerade Nebenwirkungen sein, weil Sie nicht wirklich dies in einem Test bedeutete zu tun.

Stattdessen würde ich empfehlen, den Test Umformulierung so dass Sie nicht Notwendigkeit Anfrage scoped Bohnen. Dies sollte nicht schwierig sein, sollte die Lebensdauer des @Test nicht länger sein als die Lebensdauer eines anforderungs scoped Bohne, wenn Sie in sich geschlossene Tests schreiben. Denken Sie daran, es gibt keine Notwendigkeit, den Scoping-Mechanismus zu testen, ist es Teil des Frühlings ist und man kann annehmen, dass es funktioniert.

Andere Tipps

Lösung für Frühling 3.2 oder neuer

Frühling ab Version 3.2 bietet Unterstützung für die Sitzung / Anfrage Bohnen für Integrationstests scoped .

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

Lesen Sie mehr: Anfrage und Session Beans scoped


Lösung für den Frühling vor 3.2 mit Hörer

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

Lösung für den Frühling vor 3.2 mit benutzerdefinierten Bereichen

@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

}

oder mit XML-Konfiguration

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>

Quellcode

Der Quellcode für alle vorgestellten Lösungen:

Ich habe mehrere Lösungen ausprobiert, darunter @ Marius-Lösung mit dem „WebContextTestExecutionListener“, aber es hat für mich nicht funktionieren, da dieser Code des Anwendungskontext geladen, bevor die Anforderung Umfang zu schaffen.

Die Antwort, die mir am Ende geholfen ist nicht neu, aber es ist gut: http://tarunsapra.wordpress.com/2011/06 / 28 / jUnit-Feder-Session-and-Anfrage-scope-Bohnen /

Ich habe einfach den folgenden Ausschnitt meines (Test) Anwendungskontext:

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

Viel Glück!

Eine Lösung, getestet mit Spring 4, für, wenn Sie Anfrage-scoped Bohnen benötigen, aber machen keine Anfragen über 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()));
    }

    // ...

Dies ist immer noch eine offene Frage:

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

Ich war in der Lage zu bekommen dies durch die Definition eines benutzerdefinierten Kontext loader in

wie skizzierte (meist) zu arbeiten,

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

Test-Anfrage-Scoped Bohnen mit Frühling erklären sehr gut, wie registrieren und einen benutzerdefinierten Bereich mit Feder zu erstellen.

Auf dem Punkt gebracht, als Ido Cohn erklärte, es ist genug, um folgend den Kontext Konfiguration hinzuzufügen:

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

Statt die vordefinierte SimpleThreadScope verwenden, basierend auf Thread, es ist auch einfach, eine Gewohnheit eine zu implementieren, wie in dem Artikel erläutert.

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

MariuszS‘Lösung funktioniert, außer ich nicht die Transaktion richtig begangen werden könnte.

Es scheint, die neu veröffentlichten 3.2 ist endlich gemacht Testanfrage / Session Beans Bürger erster Klasse scoped. Ist hier ein paar Blogs für weitere Details.

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

Sam Brannen Frühling Framework 3.2 RC1: New-Prüfung Eigenschaften

NICHT die Dokumentation zu lesen treibt manchmal ein verrückt. Fast.

Wenn Sie mit kurzlebigen Bohnen (Request-Scope zum Beispiel), werden Sie wahrscheinlich müssen auch Ihren faul init Standard ändern! Andernfalls wird der WebAppContext fehl Sie etwas zu laden und sagen, über fehlende Anfrage Umfang, was natürlich fehlt, weil der Kontext noch geladen wird!

scroll top