2011-01-04 14 views
8

Estoy buscando las mejores prácticas para configurar las pruebas de unidad e integración con Spring.Spring: unidad y pruebas de integración

que suelen utilizar 3 tipos de pruebas:

  • pruebas "reales" de la unidad (no hay dependencias)
  • pruebas se ejecutan ya sea como db "unidad" de prueba (en memoria, llamadas locales, simulacros objetos , ...) o como prueba de integración (db persistente, llamadas remotas, ...)
  • pruebas se ejecutan sólo como pruebas de integración

Actualmente sólo tengo pruebas de la segunda categ ory, que es la parte difícil. I puesta a punto de una clase de prueba base como:

@ContextConfiguration(locations = { "/my_spring_test.xml" }) 
public abstract class AbstractMyTestCase extends AbstractJUnit4SpringContextTests 

Y pruebas de "unidad" como:

public class FooTest extends AbstractMyTestCase 

con atributos autowired.

¿Cuál es la mejor manera de ejecutar la prueba en un entorno diferente (prueba de integración)? ¿Subclase la prueba y anule la ContextConfiguration?

@ContextConfiguration(locations = { "/my_spring_integration_test.xml" }) 
public class FooIntegrationTest extends FooTest 

¿Funcionaría (actualmente no puedo probarlo fácilmente)? El problema con este enfoque es que "@ContextConfiguration (locations = {" /my_spring_integration_test.xml "})" se duplica mucho.

¿Alguna sugerencia?

Saludos, Florian

+0

¿Encontró una solución que se ajusta a su? – FrVaBe

Respuesta

2

me gustaría ir con esta versión:

ContextConfiguration(locations = { "/my_spring_test.xml" }) 
public abstract class AbstractMyTestCase extends AbstractJUnit4SpringContextTests 

y en my_spring_test.xml, me gustaría usar el mecanismo PropertyPlaceHolderConfigurer.

Ejemplo de APP:

<context:property-placeholder 
    system-properties-mode="OVERRIDE" 
    location="classpath:test.properties" /> 

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
    destroy-method="close"> 
    <property name="driverClassName" value="${test.database.driver}" /> 
    <property name="url" value="${test.database.server}" /> 
    <property name="username" value="${test.database.user}" /> 
    <property name="password" value="${test.database.password}" /> 
</bean> 

<bean id="entityManagerFactory" 
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
    <property name="persistenceUnitName" value="test" /> 
    <property name="dataSource" ref="dataSource" /> 
    <property name="persistenceXmlLocation" 
      value="classpath:META-INF/persistence.xml" /> 
    <property name="jpaVendorAdapter"> 
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
    <property name="showSql" value="false" /> 
    <property name="generateDdl" value="${test.database.update}" /> 
    <property name="database" value="${test.database.databasetype}" /> 
</bean> 
    </property> 
</bean> 

Ahora todo lo que necesita hacer es tener diferentes versiones de test.properties en la ruta de clase para la memoria y en las pruebas de integración real (y por supuesto las clases de controladores respectivos necesitan Ser presente). Incluso puede configurar propiedades del sistema para sobrescribir los valores de las propiedades.


Si desea preparar esto con maven, encontrará que copiar archivos con maven no es trivial. Necesitará una forma de ejecutar código, las opciones estándar son maven-antrun-plugin y gmaven-maven-plugin.

De cualquier modo: tiene dos archivos de configuración, p. en src/main/config y agregue dos ejecuciones de complementos, una en la fase generate-test-resources y otra en la fase pre-integration-test.Aquí está la versión GMaven:

<plugin> 
    <groupId>org.codehaus.gmaven</groupId> 
    <artifactId>gmaven-plugin</artifactId> 
    <version>1.3</version> 
    <executions> 
     <execution> 
      <phase>pre-integration-test</phase> 
      <goals> 
       <goal>execute</goal> 
      </goals> 
      <configuration> 
      <source> 
      new File(
       pom.build.testOutputDirectory, 
       "test.properties" 
      ).text = new File(
         pom.basedir, 
         "src/main/config/int-test.properties" 
      ).text; 
      </source> 
      </configuration> 
     </execution> 
     <execution> 
      <phase>generate-test-resources</phase> 
      <goals> 
       <goal>execute</goal> 
      </goals> 
      <configuration> 
      <source> 
      new File(
       pom.build.testOutputDirectory, 
       "test.properties" 
      ).text = new File(
         pom.basedir, 
         "src/main/config/memory-test.properties" 
      ).text; 
      </source> 
      </configuration> 
     </execution> 
    </executions> 
</plugin> 
+0

Sí, ya estoy usando el marcador de posición de propiedad y pensé en reemplazar el archivo en el classpath. ¿Cuál es la forma más fácil de hacer esto con Maven? ¿Separar proyectos/dependencias que solo contengan un solo archivo de propiedades y luego modificar las dependencias de alguna manera dependientes de la fase/perfil? – Puce

+0

@Puce código maven agregado –

+0

Gracias, aunque no quiero presentar Groovy al proyecto en este momento. Creo que el problema es que a Maven le falta soporte adecuado para la prueba de integración: http://willcode4beer.com/opinion.jsp?set=maven2_integration-test Comprobaré si puedo hacerlo con Mave-antrun-pluging o incluso con el Complemento de recursos de Maven (recursos: recursos de copia) – Puce

4

que extendieron el GenericXmlContextLoader

public class MyContextLoader extends GenericXmlContextLoader {

y overrote el método

protected String[] generateDefaultLocations(Class<?> clazz)

para recoger los nombres de los archivos de configuración de un directorio que puedo especifique por una propiedad del sistema (-Dtest.config =).

También he modificado el método follwowing a no modificar ubicaciones

@Override 
protected String[] modifyLocations(Class<?> clazz, String... locations) { 
    return locations; 
} 

utilizo este cargador contexto como este

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(loader = MyContextLoader.class) 
public class Test { .... } 

Ejecución de la prueba con un SystemProperty indicando la fuente de los archivos de configuración permite ahora debes usar configuraciones completamente diferentes.

El uso de una propiedad del sistema es, por supuesto, una sola estrategia para especificar la ubicación de la configuración. Puede hacer lo que quiera en generateDefaultLocations().


EDIT:

Esta solución le permite utilizar configuraciones de contexto de aplicación completas diferentes (por ejemplo, para objetos de imitación) y no sólo diferentes propiedades. No necesita un paso de compilación para implementar todo en su ubicación de "classpath". Mi implementación concreta también usó el nombre de los usuarios como predeterminado para buscar un directorio de configuración (src/test/resources/{user}) si no se proporciona ninguna propiedad del sistema (hace que sea fácil mantener entornos de prueba específicos para todos los desarrolladores en el proyecto).

El uso de PropertyPlaceholder todavía es posible y recomendado.


EDITAR:

primavera Versión 3.1.0 apoyará XML profiles/Environment Abstraction que es similar a mi solución y permitirá a la elección de los archivos de configuración para diferentes entornos/perfiles.

+0

Esto suena interesante. ¡Gracias! – Puce

+0

@Puce Eche un vistazo a las nuevas funciones en la próxima versión de Spring 3.1.0 (vea mi última EDIT). – FrVaBe

0

No he tenido éxito en el uso del contexto de Spring 3.x: etiqueta de propiedad-marcador de posición. He utilizado la etiqueta grano de la moda antigua, junto con un archivo de propiedades y fue capaz de establecer una conexión entre mi código y mi base de datos de este modo:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> 
    <property name="location" value="/com/my/package/database.properties"/> 
</bean> 


<bean id="myDatasource" class="oracle.ucp.jdbc.PoolDataSourceFactory" 
    factory-method="getPoolDataSource"> 
    <property name="URL" value="${JDBC_URL}"/> 
    <property name="user" value="${JDBC_USERNAME}"/> 
    <property name="password" value="${JDBC_PASSWORD}"/> 
    <property name="connectionFactoryClassName" 
     value="oracle.jdbc.pool.OracleConnectionPoolDataSource"/> 
    <property name="ConnectionPoolName" value="SCDB_POOL"/> 
    <property name="MinPoolSize" value="5"/> 
    <property name="MaxPoolSize" value="50"/> 
    <property name="connectionWaitTimeout" value="30"/> 
    <property name="maxStatements" value="100"/> 
</bean> 

He aquí un ejemplo de las propiedades del archivo:

JDBC_URL=jdbc:oracle:thin:@myDB:1521:mySchema 
JDBC_USERNAME=username 
JDBC_PASSWORD=password 

Entonces creé mi prueba unitaria de este modo:

@ContextConfiguration(locations = {"/com/my/pkg/test-system-context.xml"}) 
@RunWith(SpringJUnit4ClassRunner.class) 
public class HeaderDaoTest { 

    @Autowired 
    HeaderDao headerDao; 

    @Test 
    public void validateHeaderId() { 
     int headerId = 0; 

     headerId = headerDao.getHeaderId(); 

     assertNotSame(0,headerId); 
    } 

} 

que trabajó para mí, pero todo el mundo hace las cosas un poco diferente. Espero que esto ayude.

0

Recientemente me encontré con el mismo problema y mirando el documentation for the @ContextConfiguration annotation, noté la opción inheritLocations.

Al agregar esto a mi clase, p.

@ContextConfiguration(locations = { "/my_spring_integration_test.xml" }, inheritLocations=false) 
public class FooIntegrationTest extends FooTest 

Descubrí que podía anular ContextConfiguration como lo deseaba.

Cuestiones relacionadas