demande des haricots scope dans les tests de printemps
-
18-09-2019 - |
Question
Je voudrais utiliser la demande scope haricots dans mon application. J'utilise junit4 pour les tests. Si je tente de créer un dans un test comme celui-ci:
@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);
}
Avec la définition de haricot suivant:
<?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>
Et je reçois:
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'
Alors j'ai trouvé ce blog qui semblait utile: http://www.javathinking.com/2009 /06/no-scope-registered-for-scope-request_5.html
Mais je remarquai qu'il utilise AbstractDependencyInjectionSpringContextTests qui semble être dépréciée au printemps 3.0.
J'utilise Spring 2.5 à ce moment, mais je pensais qu'il ne devrait pas être trop difficile de changer cette méthode pour utiliser AbstractJUnit4SpringContextTests
les documents suggèrent (ok la documentation lien vers la version 3.8, mais je suis en utilisant 4.4). Alors je change la
test pour étendre AbstractJUnit4SpringContextTests ... même message. Même problème. Et maintenant la méthode prepareTestInstance () Je veux
à remplacer ne soit pas défini. OK, peut-être que je vais mettre les registerScope appelle un autre ... Alors, je l'ai lu plus sur
La solution Le test passe parce qu'il ne fait rien:) Lorsque vous omettez l'annotation Lorsque vous déclarez Modifier votre test pour qu'il le fait, et il échouera: Avec votre Maintenant, à votre problème d'origine. Si vous voulez essayer l'enregistrement du périmètre de la requête avec votre contexte de test, puis un coup d'oeil au code source de Vous pouvez essayer, et de voir comment vous allez, mais il pourrait y avoir des effets secondaires bizarres, parce que vous n'êtes pas vraiment l'intention de faire cela dans un test. Au lieu de cela, je recommande reformulant votre test afin que vous ne faites pas besoin demande scope haricots. Cela ne devrait pas être difficile, le cycle de vie du @TestExecutionListeners
, enregistre Spring 3 auditeurs par défaut, dont un appelé DependencyInjectionTestExecutionListener
. Ceci est l'auditeur responsable de l'analyse de votre classe de test à la recherche de choses à injecter, y compris les annotations de @Resource
. Cet auditeur a essayé d'injecter tObj
et échoue, en raison de la portée non définie. @TestExecutionListeners({})
, vous supprimez l'enregistrement de la DependencyInjectionTestExecutionListener
, et donc le test gets tObj
jamais injecté du tout, et parce que votre test ne vérifie pas l'existence de tObj
, il passe. @Test
public void testBean() {
assertNotNull("tObj is null", tObj);
}
@TestExecutionListeners
vide, le test passe parce que rien ne se passe . WebApplicationContextUtils.registerWebApplicationScopes()
, vous trouverez la ligne: beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
@Test
ne devrait pas être plus long que le cycle de vie d'un haricot scope demande, si vous écrivez des tests autonomes. Rappelez-vous, il n'y a pas besoin de tester le mécanisme de détermination de la portée, il fait partie du printemps et vous pouvez supposer qu'il fonctionne.
Autres conseils
Solution pour le printemps 3.2 ou plus récent
Printemps à partir de la version 3.2 fournit un soutien pour scope séance / demande des haricots pour les tests d'intégration .
@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));
}
}
En savoir plus: Demande et session Scoped beans
Solution pour le printemps avant 3.2 avec l'auditeur
@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());
}
}
}
Solution pour le printemps avant 3.2 avec champs personnalisés
@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 avec la configuration 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>
Code source
Le code source pour toutes les solutions présentées:
J'ai essayé plusieurs solutions, y compris @ la solution de Marius avec le « WebContextTestExecutionListener », mais il ne fonctionne pas pour moi, car ce code chargé le contexte d'application avant de créer le périmètre de la requête.
La réponse qui m'a aidé à la fin n'est pas une nouvelle, mais il est bon: http://tarunsapra.wordpress.com/2011/06 / 28 / JUnit-printemps-session et demande de portée en grains /
J'ai simplement ajouté l'extrait suivant à mon (test) contexte d'application:
<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>
Bonne chance!
Une solution, testée avec Spring 4, lorsque vous avez besoin d'haricots scope demande, mais ne faites aucune demande 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()));
}
// ...
Il est encore une question ouverte:
https://jira.springsource.org/browse/SPR-4588
J'ai pu obtenir ce travail (la plupart du temps) en définissant un contexte personnalisé chargeur comme indiqué dans
Demande d'essai-Scoped Fèves au printemps explique très bien comment vous inscrire et créer un champ personnalisé avec le printemps.
En un mot, comme Ido Cohn a expliqué, il suffit d'ajouter ce qui suit à la configuration du contexte du texte:
<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>
Au lieu d'utiliser le SimpleThreadScope prédéfini, basé sur ThreadLocal, il est également facile à mettre en œuvre un personnalisé, comme expliqué dans l'article.
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;
}
}
solution « MariuszS fonctionne, sauf que je ne pouvais pas me la transaction validée correctement.
Il semble que la nouvelle version 3.2 a finalement fait la demande de test / séance scope haricots citoyens de première classe. Voici quelques blogs pour plus de détails.
deRossen Stoyanchev Spring Framework 3.2 RC1: test Framework Spring MVC
Sam Brannen Printemps cadre 3.2 RC1: Nouveau test Caractéristiques
PAS la lecture des documents conduit parfois un fou. Presque.
Si vous utilisez des haricots plus courte durée (portée de la demande par exemple), vous avez probablement besoin aussi de changer votre défaut init paresseux! Sinon, le WebAppContext ne parviendra pas à charger et vous dire quelque chose sur la portée de la demande manquante, ce qui est évidemment manquante, parce que le contexte est toujours en cours de téléchargement!