test run mvc example java spring junit spring-mvc spring-test

java - run - solicitar frijoles con fondo en las pruebas de primavera



spring test example (8)

Me gustaría utilizar beans de ámbito de solicitud en mi aplicación. Yo uso JUnit4 para probar. Si intento crear uno en una prueba como esta:

@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 siguiente definición de frijol:

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

Y entiendo:

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

Así que encontré este blog que me pareció útil: http://www.javathinking.com/2009/06/no-scope-registered-for-scope-request_5.html

Pero noté que usa AbstractDependencyInjectionSpringContextTests que parece estar en desuso en Spring 3.0. Utilizo Spring 2.5 en este momento, pero pensé que no debería ser demasiado difícil cambiar este método para usar AbstractJUnit4SpringContextTests como lo sugieren los documentos (ok el enlace de documentos a la versión 3.8 pero estoy usando 4.4). Así que cambio la prueba para extender AbstractJUnit4SpringContextTests ... el mismo mensaje. El mismo problema. Y ahora el método prepareTestInstance () que deseo sobrescribir no está definido. OK, tal vez ponga esas llamadas registerScope en otro lugar ... Así que leo más sobre TestExecutionListeners y creo que sería mejor, ya que no quiero tener que heredar la estructura del paquete de primavera. Así que cambié mi prueba a:

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

esperando tener que crear un oyente personalizado pero yo cuando lo ejecuté. ¡Funciona! Genial, pero ¿por qué? No veo dónde alguno de los oyentes stock registra el alcance de la solicitud o el alcance de la sesión, y ¿por qué lo harían? no hay nada que decir, quiero eso todavía, esto podría no ser un código Test for Spring MVC ...


Solución para Spring 3.2 o posterior

El resorte que comienza con la versión 3.2 proporciona soporte para beans de ámbito sesión / solicitud para pruebas de integración .

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

Leer más: Solicitud y habas de ámbito de sesión

Solución para Spring before 3.2 with listener

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

Solución para Spring before 3.2 con ámbitos personalizados

@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 configuración 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>

Código fuente

Código fuente para todas las soluciones presentadas:



He intentado varias soluciones, incluida la solución de @Marius con el "WebContextTestExecutionListener", pero no funcionó, ya que este código cargó el contexto de la aplicación antes de crear el alcance de la solicitud.

La respuesta que me ayudó al final no es nueva, pero es buena: http://tarunsapra.wordpress.com/2011/06/28/junit-spring-session-and-request-scope-beans/

Simplemente agregué el siguiente fragmento a mi contexto de aplicación (prueba):

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

¡Buena suerte!


La prueba pasa porque no está haciendo nada :)

Cuando omite la anotación @TestExecutionListeners , Spring registra 3 detectores predeterminados, incluido uno llamado DependencyInjectionTestExecutionListener . Este es el oyente responsable de escanear tu clase de prueba en busca de cosas para inyectar, incluidas @Resource anotaciones @Resource . Este oyente intentó inyectar tObj y falla debido al alcance indefinido.

Cuando declara @TestExecutionListeners({}) , suprime el registro de DependencyInjectionTestExecutionListener , por lo que la prueba nunca obtiene inyectado en absoluto, y como su prueba no verifica la existencia de tObj , se pasa.

Modifique su prueba para que lo haga y fallará:

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

Entonces con tus @TestExecutionListeners vacíos, la prueba pasa porque no pasa nada .

Ahora, a tu problema original. Si quiere intentar registrar el alcance de la solicitud con su contexto de prueba, entonces eche un vistazo al código fuente de WebApplicationContextUtils.registerWebApplicationScopes() , encontrará la línea:

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

Podrías probar eso, y ver cómo te va, pero podría haber efectos secundarios extraños, porque en realidad no debes hacer esto en una prueba.

En su lugar, recomendaría reformular su prueba para que no necesite pedir frijoles de ámbito determinado. Esto no debería ser difícil, el ciclo de vida de @Test no debería ser más largo que el ciclo de vida de un bean con ámbito de solicitud, si usted escribe pruebas independientes. Recuerde, no es necesario probar el mecanismo de determinación del alcance, es parte de Spring y puede suponer que funciona.



NO leer los documentos a veces lo vuelve loco. Casi.

Si está utilizando beans de vida más corta (por ejemplo, alcance de solicitud), lo más probable es que también necesite cambiar su valor predeterminado de inicio lento. De lo contrario, el WebAppContext no se cargará y le dirá algo sobre el alcance de la solicitud faltante, que por supuesto no se encuentra, ¡porque el contexto aún se está cargando!

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

Los chicos de Spring definitivamente deberían poner esa sugerencia en su mensaje de excepción ...

Si no desea cambiar el valor predeterminado, también existe la forma de anotación: coloque "@Lazy (true)" después de @Component, etc. para hacer que los singletons se inicialicen como flojos y evite crear instancias de beans con ámbito de solicitud demasiado pronto.


Una solución, probada con Spring 4, para cuando necesita beans con alcance de solicitud pero no realiza ninguna solicitud a través de 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())); } // ...


Solicitud de prueba: Scops Beans with Spring explica muy bien cómo registrarse y crear un alcance personalizado con Spring.

En pocas palabras, como Ido Cohn explicó, es suficiente agregar lo siguiente a la configuración del contexto de 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>

En lugar de utilizar SimpleThreadScope predefinido, basado en ThreadLocal, también es fácil implementar uno personalizado, como se explica en el artículo.

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