سؤال

أود الاستفادة من الطلب الفاصوليا النحافة في التطبيق الخاص بي. أنا استخدم 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);
    }

مع تعريف الفول التالي:

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

لكنني لاحظت أنه يستخدم مجردة الإبداعإصلاح التي يبدو أنها مهملة في الربيع 3.0. يمكنني استخدام الربيع 2.5 في هذا الوقت، ولكن اعتقدت أنه لا ينبغي أن يكون من الصعب للغاية تبديل هذه الطريقة لاستخدام هذه الطريقة لاستخدام abstractjunit4springcontextteststs حيث توحي المستندات (موافق على رابط المستندات إلى الإصدار 3.8 ولكني أستخدم 4.4). لذلك أقوم بتغيير الاختبار لتمديد التزلج Justjunit4springcontextts ... نفس الرسالة. نفس المشكلة. والآن طريقة الإعداد () الطريقة التي أرغب في تجاوزها غير محددة. حسنا، ربما سأضع هذه التسجيل مكالمات في مكان آخر ... لذلك قرأت المزيد عن TestExectionListeners. وأعتقد أن هذا سيكون أفضل لأنني لا أريد أن يرث ترثي بنية حزمة الربيع. لذلك غيرت اختباري إلى:

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

نتوقع أن أضطر إلى إنشاء مستمع مخصص ولكني عندما صادفها. إنها تعمل! عظيم، ولكن لماذا؟ لا أرى أين يسجل أي من مستمعي الأسهم نطاق الطلب أو نطاق الجلسة، ولماذا سيكونون؟ لا يوجد شيء أقول إنني أريد ذلك حتى الآن، قد لا يكون هذا اختبارا لرصيف الربيع MVC ...

هل كانت مفيدة؟

المحلول

يمر الاختبار لأنه لا يفعل أي شيء :)

عندما حذفت @TestExecutionListeners التوضيحية، سجلات الربيع 3 مستمعين افتراضي، بما في ذلك واحد يسمى DependencyInjectionTestExecutionListener. وبعد هذا هو المستمع المسؤول عن مسح فئة الاختبار الخاصة بك تبحث عن أشياء لحقنها، بما في ذلك @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 لا ينبغي أن يكون أطول من دورة حياة الفاصوليا المشبعة بالطلب، إذا كتبت اختبارات قائمة بذاتها. تذكر، ليس هناك حاجة لاختبار آلية النطاق، إنها جزء من الربيع ويمكنك نفترض أنها تعمل.

نصائح أخرى

حل لربيع 3.2 أو الأحدث

الربيع بدءا من الإصدار 3.2 يوفر الدعم للدورة / طلب الفاصوليا النحافة لاختبار التكامل.

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

اقرأ أكثر: طلب ودورة حبوب النحافة


حل لربيع قبل 3.2 مع المستمع

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

حل لربيع قبل 3.2 مع نطاقات مخصصة

@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

}

أو مع تكوين 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>

مصدر الرمز

شفرة المصدر لجميع الحلول المقدمة:

لقد جربت عدة حلول، بما في ذلك محلول @ Marius مع "WebContextTextExCerTener"، لكنه لم يعمل بالنسبة لي، حيث قام هذا الرمز بتحميل سياق التطبيق قبل إنشاء نطاق الطلب.

الإجابة التي ساعدتني في النهاية ليست واحدة جديدة، لكنها جيدة:http://tarunsapra.wordpress.com/2016/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>

حظ سعيد!

حل، تم اختباره مع Spring 4، لأنه عند طلب الفاصوليا المفردة بالطلب ولكن لا يقوم بأي طلبات عبر MockMVC, ، إلخ.

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

    // ...

هذا لا يزال مشكلة مفتوحة:

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

كنت قادرا على العمل (معظمها) من خلال تحديد محمل سياق مخصص كما هو موضح في

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

اختبار طلب الفاصوليا مع الربيع يوضح جيدا كيفية التسجيل وإنشاء نطاق مخصص مع الربيع.

باختصار، كما أوضح 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>

بدلا من استخدام SimplethreadCreadScope المسبق، استنادا إلى ThreadLocal، من السهل أيضا تطبيق واحد مخصص، كما هو موضح في المقالة.

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، إلا أنني لم أستطع الحصول على المعاملة المرفقة بشكل صحيح.

يبدو أن 3.2 صدر حديثا قد قدمت أخيرا طلب اختبار / جلسة فاصوليا من الدرجة الأولى. إليك بضعة بلوق لمزيد من التفاصيل.

روسين stoyanchev الإطار الربيعي 3.2 RC1: ربيع MVC الإطار

سام برانين الإطار الربيعي 3.2 RC1: ميزات اختبار جديدة

عدم قراءة المستندات تحرج أحيانا مجنونا. تقريبيا.

إذا كنت تستخدم الفاصوليا الأقصر (نطاق الطلب على سبيل المثال)، فأنت بحاجة أيضا إلى تغيير الافتراضي الخاص بك كسول! وإلا فسوف تفشل WebApPContext في تحميل وإخبارك بشيء عن نطاق الطلب المفقود، وهو بالطبع مفقود، لأن السياق لا يزال جاريا!

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

يجب أن يضع الرجال الربيعون بالتأكيد تلميح في رسالتهم الاستثناء ...

إذا كنت لا ترغب في تغيير الإعداد الافتراضي، فهناك أيضا طريقة التعليق التوضيحي: ضع "Lazy (True)" بعد accordent وما إلى ذلك لجعل المفردون تهيئ كسول وتجنب الفاصوليا المفصيلية في وقت مبكر جدا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top