2009-08-25 27 views
16

Nuestro equipo tiene cientos de pruebas de integración que llegan a una base de datos y verifican los resultados. Tengo dos clases base para todas las pruebas de integración, una para las pruebas solo de recuperación y otra para las pruebas de creación/actualización/eliminación. La clase base de solo recuperación regenera la base de datos durante el TestFixtureSetup por lo que solo se ejecuta una vez por clase de prueba. La clase base CUD regenera la base de datos antes de cada prueba. Cada clase de repositorio tiene su propia clase de prueba correspondiente.Mejores prácticas de prueba de integración

Como se puede imaginar, todo esto lleva bastante tiempo (se acercan a 7-8 minutos para ejecutarse y crecer rápidamente). Hacer que esto se ejecute como parte de nuestro CI (CruiseControl.Net) no es un problema, pero la ejecución local lleva mucho tiempo y realmente prohíbe su ejecución antes de confirmar el código.

Mi pregunta es ¿existen algunas mejores prácticas para ayudar a acelerar la ejecución de este tipo de pruebas de integración?

No puedo ejecutarlos en la memoria (a la sqlite) porque utilizamos algunas funciones específicas de la base de datos (columnas calculadas, etc.) que no son compatibles con sqlite.

Además, todo el equipo tiene que poder ejecutarlos, por lo que ejecutarlos en una instancia local de SQL Server Express o algo podría ser propenso a errores a menos que las cadenas de conexión sean las mismas para esas instancias.

¿Cómo lo está logrando en su tienda y qué funciona bien?

Gracias!

Respuesta

5

Soy un desarrollador de Java pero he tenido un problema similar. Descubrí que ejecutar una instancia de base de datos local funciona bien debido a la velocidad (no hay datos para enviar a través de la red) y porque de esta manera no tiene conflictos en su base de datos de prueba de integración.

El enfoque general que utilizamos para resolver este problema es configurar los scripts de compilación para leer las cadenas de conexión de la base de datos desde un archivo de configuración, y luego configurar un archivo por entorno. Por ejemplo, un archivo para WORKSTATION, otro para CI. A continuación, configura los scripts de compilación para leer el archivo de configuración en función del entorno especificado. Por lo tanto, las compilaciones que se ejecutan en una estación de trabajo de desarrollador se ejecutan utilizando la configuración de WORKSTATION y las compilaciones que se ejecutan en el entorno de IC utilizan la configuración de IC.

También ayuda muchísimo si se puede crear el esquema completo de la base de datos a partir de un único script, de modo que cada desarrollador puede configurar rápidamente una base de datos local para realizar pruebas. Incluso puede extender este concepto al próximo nivel y agregar el script de configuración de la base de datos al proceso de compilación, de modo que toda la configuración de la base de datos pueda programarse para mantenerse al día con los cambios en el esquema de la base de datos.

11

en NUnit se puede decorar sus clases de prueba (o métodos) con un atributo por ejemplo:

[Category("Integration")] 
public class SomeTestFixture{ 
    ... 
} 
[Category("Unit")] 
public class SomeOtherTestFixture{ 
    ... 
} 

A continuación, puede estipular en el proceso de construcción en el servidor que todas las categorías consiguen correr y sólo requieren que sus desarrolladores ejecutar un subconjunto de las categorías de prueba disponibles. Las categorías que deben ejecutar dependerán de cosas que comprenderán mejor que yo. Pero lo esencial es que pueden probar a nivel de unidad y el servidor maneja las pruebas de integración.

+1

+1 eso es más o menos lo que hacemos en mi trabajo. En nuestro CIS, las pruebas unitarias se ejecutan en cada prueba de checkin e integración una vez al día. – mezoid

+2

donde trabajo ejecutamos las pruebas de integración en cada compilación. cuanto más frecuentemente ejecute las pruebas de integración, mejor. –

+1

@Ken_Liu - seguro. A menudo se trata de equilibrar los ciclos de CPU disponibles en el servidor de CI contra el número de confirmaciones por momento. Esto variará para cada equipo/entorno de desarrollo. – grenade

3

¿Ha realizado alguna medida (usando temporizadores o similares) para determinar dónde pasan las pruebas la mayor parte de su tiempo?

Si ya sabe que la recreación de la base de datos es por lo que lleva mucho tiempo, un enfoque diferente sería regenerar la base de datos una vez y usar transacciones para preservar el estado entre las pruebas.Cada prueba de tipo CUD inicia una transacción en la configuración y realiza una reversión en el desmontaje. Esto puede reducir significativamente el tiempo dedicado a la configuración de la base de datos para cada prueba, ya que una reversión de la transacción es más barata que una recreación completa de la base de datos.

+0

Sí, he visto esta idea antes, pero ¿qué pasa si parte del código que prueba utiliza la transacción en sí (recuerde, estas son pruebas de integración)? Entonces todo se romperá, así que no creo que esto sea práctico en general. – sleske

+1

Si está realizando una administración de transacciones explícita dentro de sus métodos, tiene toda la razón. Este enfoque funciona mejor en los casos en que utiliza aspectos o un proxy para manejar transacciones. Usamos este enfoque en un proyecto basado en Spring y la configuración de prueba de integración reemplazó el proxy con la transacción administrada de prueba de unidades. Trabajado como un encanto. – henrik

3

Tenemos una instancia de SQL Server Express con la misma definición de base de datos que se ejecuta para cada máquina de desarrollo como parte del entorno de desarrollo. Con la autenticación de Windows, las cadenas de conexión son estables, sin nombre de usuario/contraseña en la cadena.

Lo que realmente nos gustaría hacer, pero aún no lo hemos hecho, es ver si podemos hacer que nuestro sistema se ejecute en SQL Server Compact Edition, que es como SQLite con el motor de SQL Server. Entonces podríamos ejecutarlos en memoria, y posiblemente en paralelo también (con múltiples procesos).

+0

mmm SQL Compact. ¡que * sería * dulce! – grenade

+0

¡me gusta la idea de eso, seguro! –

13

Mantenga las pruebas rápida (unidad) y lenta (integración) separadas, para que pueda ejecutarlas por separado. Utilice su método de prueba para agrupar/categorizar las pruebas. Si el marco de prueba no admite agrupar las pruebas, mueva las pruebas de integración a un módulo separado que solo tenga pruebas de integración.

Las pruebas rápidas solo deberían tomar unos segundos para ejecutarlas todas y deberían tener una alta cobertura de código. Este tipo de pruebas permiten a los desarrolladores refactorizar implacablemente, ya que pueden hacer un pequeño cambio y ejecutar todas las pruebas y tener mucha confianza en que el cambio no rompió nada.

Las pruebas lentas pueden tardar muchos minutos en ejecutarse y se asegurarán de que los componentes individuales funcionen bien juntos. Cuando los desarrolladores hacen cambios que posiblemente puedan romper algo que es probado por las pruebas de integración pero no por las pruebas unitarias, deben ejecutar esas pruebas de integración antes de comprometerse. De lo contrario, el servidor de CI ejecuta las pruebas lentas.

Cuestiones relacionadas