J'ai rencontré un problème qui ne peut s'expliquer que par mon manque fondamental de compréhension des installations du conteneur IoC de Spring et de la configuration du contexte. Je vous demanderai donc des éclaircissements à ce sujet.Spring JUnit4 manuel/câblage auto dilemme
Juste pour référence, une application que je suis maintaing a la pile suivante de technologies:
- Java 1.6
- Spring 2.5.6
- RichFaces 3.3.1 GA-UI
- framework Spring est utilisé pour la gestion des beans avec le module Spring JDBC utilisé pour le support DAO
- Maven est utilisé comme gestionnaire de build
- JUnit 4.4 est non w présenté comme moteur d'essai
Je suis avec effet rétroactif (sic!) des tests JUnit d'écriture pour l'application et ce qui m'a surpris est que je n'ai pas pu injecter un haricot dans une classe de test en utilisant l'injection setter sans avoir recours à @Autowire notation. Permettez-moi de fournir un exemple et des fichiers de configuration qui l'accompagnent.
La classe de test TypeTest
est très simple:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest {
@Autowired
private IType type;
@Test
public void testFindAllTypes() {
List<Type> result;
try {
result = type.findAlltTypes();
assertNotNull(result);
} catch (Exception e) {
e.printStackTrace();
fail("Exception caught with " + e.getMessage());
}
}
}
Son contexte est défini dans TestStackOverflowExample-context.xml
:
<context:property-placeholder location="classpath:testContext.properties" />
<context:annotation-config />
<tx:annotation-driven />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${db.connection.driver.class}" />
<property name="url" value="${db.connection.url}" />
<property name="username" value="${db.connection.username}" />
<property name="password" value="${db.connection.password}" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="beanDAO" class="com.example.BeanDAOImpl">
<property name="ds" ref="dataSource"></property>
<property name="beanDAOTwo" ref="beanDAOTwo"></property>
</bean>
<bean id="beanDAOTwo" class="com.example.BeanDAOTwoImpl">
<property name="ds" ref="dataSource"></property>
</bean>
<bean id="type" class="com.example.TypeImpl">
<property name="beanDAO" ref="beanDAO"></property>
</bean>
TestContext.properties
est en classpath et contient en Les données spécifiques à DB sont nécessaires pour la source de données.
Cela fonctionne comme un charme, mais ma question est - pourquoi ça ne marche pas lorsque je tente de haricots fil manuellement et effectuer l'injection setter comme dans:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest {
private IType type;
public IType getType() {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
Qu'est-ce que je manque ici? Quelle partie de la configuration est fausse ici? Lorsque je tente d'injecter manuellement les haricots via setters, test échoue parce que cette partie
result = type.findAlltTypes();
est résolu comme nulle dans l'exécution. J'ai, bien sûr, consulté le manuel de référence de Spring et essayé différentes combinaisons de configuration XML; tout ce que je pourrais conclure est que Spring était incapable d'injecter des beans parce qu'il ne parvient pas à déréférencer correctement la référence Spring Test Context mais en utilisant @Autowired cela arrive "automagiquement" et je ne vois vraiment pas pourquoi est-ce parce que JavaDoc des annotations Autowired
sa classe PostProcessor
ne le mentionne pas.
Il est également intéressant d'ajouter que le @Autowired
est utilisé ici uniquement dans les applications. Ailleurs, seul le câblage manuel est effectué, donc cela soulève également la question - pourquoi est-ce que cela fonctionne là et pas ici, dans mon test? Quelle est la partie de la configuration DI qui me manque? Comment @Autowired
obtient une référence de Spring Context?
EDIT: J'ai aussi essayé cela, mais avec les mêmes résultats:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest implements ApplicationContextAware{
private IType type;
private ApplicationContext ctx;
public TypeTest(){
super();
ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
ctx.getBean("type");
}
public IType getType() {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
D'autres idées, peut-être? J'ai trouvé un moyen sans avoir recours à l'écriture TestContextListener
ou BeanPostProcessor
. Il est étonnamment simple et il se trouve que je suis sur la bonne voie avec ma dernière modification:
1) contexte basé sur Constructor résolution:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest{
private IType type;
private ApplicationContext ctx;
public TypeTest(){
super();
ctx = new FileSystemXmlApplicationContext("/TypeTest-context.xml");
type = ctx.getBean("type");
}
public IType getType() {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
2) En mettant en œuvre l'interface ApplicationContextAware:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class TypeTest implements ApplicationContextAware{
private IType type;
private ApplicationContext ctx;
public IType getType() {
return type;
}
public void setType(IType type) {
this.type= type;
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.ctx = ctx;
type = (Type) ctx.getBean("type");
}
@Test
public void testFindAllTypes(){
//snip, snip...
}
}
Ces deux approches approchent correctement les beans.
+1, une bonne trouvaille .. – Bozho
Alors sommes-nous sûr de conclure il n'y a effectivement aucun moyen de fil manuellement des haricots de test? Je voudrais omettre d'utiliser des annotations si possible. – quantum
Bien sûr, cela peut être fait. Au printemps, presque tout peut être fait. Vous devez écrire votre propre a) BeanPostProcessor ou b) TestExecutionListener, recherchez le bean pour votre classe de test et connectez-le en utilisant AutowireCapableBeanFactory. –