2010-10-13 10 views
13

Quiero limpiar la base de datos después de cada caso de prueba sin deshacer la transacción. He intentado con DBUnit DatabaseOperation.DELETE_ALL, pero no funciona si una eliminación viola una restricción de clave externa. Sé que puedo desactivar las comprobaciones de claves externas, pero eso también desactivaría las comprobaciones de las pruebas (que quiero evitar).JPA - Cómo truncar tablas entre pruebas unitarias

Estoy usando JUnit 4, JPA 2.0 (Eclipselink) y la base de datos en memoria de Derby. ¿Algunas ideas?

Gracias, Theo

+0

¿Por qué necesita limpiar la base de datos después de cada prueba? –

+4

Para evitar efectos secundarios. Quiero que cada prueba se ejecute aislada una de la otra. – Theo

Respuesta

5

Simple: antes de cada prueba, comience una nueva transacción y después de la prueba, deshagala.Eso le dará la misma base de datos que tenía antes.

Asegúrese de que las pruebas no creen nuevas transacciones; en su lugar reutilizar el existente.

+1

Sí, pero de esa manera, no podrá capturar ningún error que ocurra durante la confirmación de la transacción (por ejemplo, si prueba relaciones, etc.). – Theo

+2

Confirmar solo cierra la transacción y hace que los datos en la base de datos sean permanentes. Si tiene errores FK, los obtiene cuando * descarga * la sesión. No es necesario comprometerse. –

+0

OK, eso significa que un color tiene el mismo efecto que el compromiso, excepto que las transacciones se cierran y los datos se hacen permanentes en el DB. Lo probaré. Gracias – Theo

1

estoy un poco confundido en cuanto DBUnit se reiniciará la base de datos a un estado conocido antes de cada prueba.

También recommend as a best practice no limpiar o cambiar los datos después de la prueba.

Así que si es la limpieza lo que debes hacer es preparar la base de datos para la siguiente prueba, no me molestaría.

+0

En realidad, no estoy usando DBUnit. Acabo de probar la operación DELETE_ALL mencionada. Estoy llenando mi base de datos en un método JUnit @Before. ¿Es DBUnit un ajuste perfecto con JPA? – Theo

+0

Uso Unitils que es un rapero alrededor de dbunit. Usted crea un conjunto de datos con las cosas que le interesan (en xml: nadie es perfecto), agrega un par de anotaciones a su clase de prueba y listo. Ver: http://www.unitils.org/tutorial.html#Testing_with_JPA –

2

Sí, la prueba en la transacción haría su vida mucho más fácil, pero si la transacción es lo suyo, entonces necesita implementar transacción (es) de compensación durante la limpieza (en @After). Suena laborioso y podría ser, pero si se aborda adecuadamente puede terminar con un conjunto de métodos de ayuda (en pruebas) que compensan (limpieza) los datos acumulados durante @Before y pruebas (utilizando JPA o JDBC directo, lo que sea lógico).

Por ejemplo, si se utiliza JPA y llama a crear métodos en entidades durante las pruebas puede utilizar (usando AOP si te apetece o simplemente métodos de ensayo auxiliares como nosotros) un patrón en todas las pruebas de:

  1. pista los identificadores de todas las entidades que se han creado durante la prueba
  2. ellos se acumulan en orden creado
  3. entidad repetición borra de estas entidades en el orden inverso en @After
+0

Este enfoque suena bien, pero todavía es demasiado trabajo para mi gusto. Incluso si realiza un seguimiento de las entidades creadas, las cosas pueden salir mal si las elimina en el orden incorrecto (debido a problemas de integridad referencial). – Theo

+0

Sí, es demasiado elaborado para suites de prueba más pequeñas. Para evitar problemas de RI, las entidades se eliminan en orden invertido para su creación. El principal beneficio es que, una vez hecho esto, la limpieza de pruebas se vuelve transparente y no genera gastos en los desarrolladores. Cuantas más pruebas, más sentido tiene. Otro beneficio: todos los desarrolladores utilizan el mismo marco para crear datos de prueba; de lo contrario, sus pruebas no funcionarán o romperán algo. – topchef

0

Mi configuración es bastante similar: es Derby (integrado) + OpenJPA 1.2.2 + DBUnit. Así es como yo manejo las pruebas de integración para mi tarea actual: en cada método @Before corro 3 guiones:

  1. gota DB — una secuencia de comandos SQL que cae todas las tablas.
  2. Cree DB — un script SQL que los vuelva a crear.
  3. Una secuencia de comandos XML de unidad de base de datos específica de prueba para completar los datos.

Mi base de datos tiene solo 12 tablas y el conjunto de datos de prueba no es muy grande, ya sea — aproximadamente 50 registros. Cada script tarda unos 500 ms en ejecutarse y los mantengo de forma manual cuando las tablas se agregan o modifican.

Este enfoque probablemente no se recomienda para probar bases de datos grandes, y tal vez ni siquiera puede considerarse una buena práctica para las pequeñas; sin embargo, tiene una ventaja importante sobre la reversión de la transacción en el método @After: en realidad puede detectar lo que sucede en la confirmación (como entidades separadas persistentes o excepciones de bloqueo optimista).

+0

Gracias, gracias por su respuesta. Como ha dicho, este enfoque no es muy fácil de mantener. Quiero evitar cualquier script externo ... – Theo

18

La forma más sencilla de hacerlo es probablemente utilizar el método nativeQuery jpa.

@After 
public void cleanup() { 
    EntityManager em = entityManagerFactory.createEntityManager(); 
    em.getTransaction().begin(); 
    em.createNativeQuery("truncate table person").executeUpdate(); 
    em.createNativeQuery("truncate table preferences").executeUpdate(); 
    em.getTransaction().commit(); 
} 
+0

'truncar' es la instrucción DDL y no es necesario comenzar la transacción, todos los DDL se confirman automáticamente. – gadon

+3

Lo hubiera aceptado, pero createNativeQuery requirió una transacción. De lo contrario, arrojaba una excepción. Al menos ese fue el caso en Hibernate. –

+0

@gadon: Eso depende de la base de datos. Al menos PostgreSQL, DB2 e Informix tienen (algunos) soporte para DDL transaccional. Oracle, sin embargo, no. – sleske