Me encontré con un problema que solo puede explicarse por mi falta fundamental de comprensión de las instalaciones de contenedor IoC de Spring y la configuración de contexto, por lo que pediría una aclaración con respecto a esto.Primavera JUnit4 dilema de manual/auto-cableado
Sólo como referencia, una aplicación que estoy maintaing tiene la siguiente pila de tecnologías:
- Java 1.6
- primavera 2.5.6
- RichFaces 3.3.1 GA-IU
- marco de Primavera se utiliza para la gestión de beans con módulo Spring JDBC utilizado para soporte DAO
- Maven se utiliza como administrador de compilación
- JUnit 4.4 no es w introducido como motor de prueba
Soy retroactivamente (sic!) las pruebas de escritura JUnit para la aplicación y lo sorprendió mí es que yo no era capaz de inyectar un grano en una clase de prueba mediante inyección de setter sin recurrir a @Autowire notación.
Permítame proporcionarle la configuración de un ejemplo y los archivos de configuración que lo acompañan.
La clase de prueba TypeTest
es muy 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());
}
}
}
Su contexto se define en 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 la ruta de clase y contiene en datos específicos de db ly necesarios para el origen de datos.
Esto funciona como un encanto, pero mi pregunta es - ¿por qué no funciona cuando intento habas manual de alambre y llevar a cabo la inyección de la moda como en:
@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é me estoy perdiendo aquí? ¿Qué parte de la configuración está mal aquí? Cuando intento de inyectar manualmente los granos a través de los emisores, prueba falla porque esta parte
result = type.findAlltTypes();
se resuelve como nulo en tiempo de ejecución. Por supuesto, consulté el manual de referencia de Spring y probé varias combinaciones de configuración XML; todo lo que pude concluir es que Spring no pudo inyectar frijoles porque de alguna manera no eliminó correctamente la referencia de Spring Test Context pero al usar @Autowired esto sucede "automágicamente" y realmente no puedo ver por qué es que JavaDoc tanto de la anotación Autowired
y su clase PostProcessor
no menciona esto.
También vale la pena agregar es el hecho de que @Autowired
se utiliza en la aplicación solamente aquí. En otros lugares solo se realiza el cableado manual, por lo que también surge una pregunta: ¿por qué está funcionando allí y no aquí, en mi prueba? ¿Qué parte de la configuración DI me falta? ¿Cómo obtiene @Autowired
una referencia de Spring Context?
EDIT: También he probado esto, pero con el mismo resultado:
@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...
}
}
alguna otra idea, tal vez?
EDIT2: He encontrado una manera sin recurrir a la escritura propia TestContextListener
o BeanPostProcessor
. Es sorprendentemente simple y resulta que yo estaba en el camino correcto con mi última edición:
1) contexto basado en Constructor resolver:
@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) Mediante la implementación de la interfaz 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...
}
}
Ambos enfoques se aproximan a los beans.
1, buen hallazgo .. – Bozho
nosotros también seguro concluir en realidad no habrá manera de ¿Alimentar manualmente los granos de prueba? Me gustaría omitir el uso de anotaciones si es posible. – quantum
Por supuesto que se puede hacer. En primavera, casi todo se puede hacer. Debería escribir su propio a) BeanPostProcessor o b) TestExecutionListener, busque el bean para su clase de prueba y conéctelo utilizando AutowireCapableBeanFactory. –