Ich stieß auf ein Problem, das nur mit meinem grundlegenden Mangel an Verständnis der Spring IoC Container-Einrichtungen und Kontext-Setup erklärt werden kann, so würde ich um Klärung bitten.Spring JUnit4 manual-/auto-wiring dilemma
nur als Referenz, eine Anwendung, die ich die folgenden Stapel von Technologien am maintaing:
- Java 1.6
- Frühling 2.5.6
- Richfaces 3.3.1-GA UI
- Spring-Framework wird für Bean Management mit Spring verwendet JDBC Modul für DAO Unterstützung
- Maven wird als Build Manager verwendet
- JUnit 4.4 ist nicht w als Testmotor eingeführt
Ich bin rückwirkend (sic!) Schreiben JUnit-Tests für die Anwendung und was überrascht mich ist, dass ich, ohne auf eine Bohne in einer Testklasse zu injizieren, durch die Verwendung Setter Injektion nicht in der Lage @Autowire-Notation.
Lassen Sie mich einrichten, ein Beispiel und die begleitenden Konfigurationsdateien einrichten.
Die Testklasse TypeTest
ist wirklich einfach:
@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());
}
}
}
ist sein Kontext in TestStackOverflowExample-context.xml
definiert:
<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
in Classpath und enthält auf ly db-spezifische Daten, die für die Datenquelle benötigt werden.
Dies funktioniert wie ein Charme, aber meine Frage ist - warum es nicht funktioniert, wenn ich manuell Draht Bohnen versuchen und Setter-Injektion durchführt, wie in:
@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...
}
}
Was bin ich hier? Welcher Teil der Konfiguration ist hier falsch? Wenn ich versuche, Beans manuell über Setter zu injizieren, schlägt der Test fehl, weil dieser Teil
result = type.findAlltTypes();
in Runtime als null aufgelöst wird. Ich habe natürlich das Spring-Referenzhandbuch konsultiert und verschiedene Kombinationen der XML-Konfiguration ausprobiert; alles, was ich schließen konnte, ist, dass Spring nicht in der Lage war, Beans zu injizieren, weil es die Spring Test Context-Referenz nicht richtig dereferenziert, aber bei Verwendung von @Autowired geschieht dies "automatisch" und ich kann wirklich nicht sehen, warum das so ist, weil JavaDoc sowohl Autowired
Annotation und seine PostProcessor
Klasse erwähnt dies nicht.
Ebenfalls wert ist die Tatsache, dass @Autowired
in der Anwendung nur hier verwendet wird. Anderswo wird nur manuelle Verdrahtung durchgeführt, so bringt dies auch Frage hervor - warum funktioniert es dort und nicht hier, in meinem Test? Welchen Teil der DI-Konfiguration fehlt mir? Wie erhält @Autowired
Bezug auf Spring Context?
EDIT: Ich habe dies auch versucht, aber mit gleichen Ergebnissen:
@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...
}
}
Jede andere Ideen, vielleicht?
EDIT2: Ich habe einen Weg gefunden, ohne eigene TestContextListener
oder BeanPostProcessor
schreiben zu schreiben. Es ist überraschend einfach und es stellt sich heraus, dass ich auf dem richtigen Weg mit meinem zuletzt war:
1) Constructor-basierten Kontext Lösung:
@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) Durch die Implementierung ApplicationContextAware Schnittstelle:
@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...
}
}
Beide dieser Ansätze ordnungsgemäß Beans Bohnen.
+1, gut finden .. – Bozho
So sind uns sicher dort zu schließen, ist effektiv keine Möglichkeit, Testbohnen manuell verdrahten? Ich möchte auf Anmerkungen verzichten, wenn es überhaupt möglich ist. – quantum
Natürlich kann es gemacht werden. Im Frühling kann fast alles gemacht werden. Sie müssten Ihre eigenen a) BeanPostProcessor oder b) TestExecutionListener schreiben, suchen Sie nach der Bean für Ihre Testklasse und verbinden Sie sie mit der AutowireCapableBeanFactory. –