Base de datos no transaccional y pruebas de integración

8

Un problema que creo que voy a encontrar con mis pruebas de integración es tener múltiples pruebas accediendo a la misma base de datos. Si bien esto no es un problema ahora, sé que tenemos varias aplicaciones aquí que acceden a la misma base de datos y solo estoy tratando de pensar en una forma de prevenir este problema antes de que ocurra.

Una idea que he visto mucho es usar transacciones. En el inicio, inicia una transacción y luego, en el desmontaje, revierte la transacción. Esto significa que múltiples pruebas acceden a las mismas tablas de la base de datos y no se afectarán entre sí, lo cual es excelente. El problema que tengo es que, en mi caso, el 85-95% de las tablas con las que estoy trabajando en MySQL son MyISAM que no admiten transacciones.

¿Hay alguna forma de evitar los motores de almacenamiento que no admiten transacciones pero que aún permiten que varias pruebas accedan a las mismas tablas sin que se afecten entre sí? Por lo que escuché, el marco de prueba de ruby ​​on rails utiliza las transacciones de esta manera, ¿cómo pueden solucionar este problema (o lo hacen)?

ryanzec
fuente
¿Ejecutas pruebas simultáneamente?
JeffO
Yo podría. No ejecutaría pruebas simultáneas del mismo proyecto, pero tenemos varios proyectos que acceden a la misma base de datos / tablas. Pude ver 2 proyectos ejecutando sus pruebas al mismo tiempo.
ryanzec
1
+1 Es aún más difícil si necesita ejecutar pruebas simultáneamente.
KLE

Respuestas:

4

En mi empresa, hubo ese debate: la persistencia es un gran problema para las pruebas.

Hay algunos trucos que te llevarán a la mitad. Decidimos no perder nuestro tiempo con ellos:

  • utilizando transacciones que revierte . Eso falla como:
    • A veces abre una transacción aislada (dentro de una más general), por lo que se ha cerrado antes de que su código de prueba pueda controlarla
    • No puedes probar tu verdadera persistencia. (Qué sucede cuando una prueba cierra una transacción y ejecuta una consulta compleja afectada por esos datos en una transacción diferente; o guarda datos y los carga de nuevo más adelante).
    • No puede probar su código completamente (solo las capas por debajo del nivel de transacción; no es lo suficientemente bueno si considera los inconvenientes de las pruebas de integración).
  • utilizando una nueva base de datos limpia para cada prueba . Eso también falla como:
    • nuestro conjunto de pruebas pronto llegará a unos pocos miles de pruebas (tenemos grandes equipos). Configurar una nueva base de datos requiere minutos en el mejor de los casos (ya que tenemos muchos catálogos y parámetros que forman parte de cada versión). Nuestro conjunto de pruebas tardaría meses en terminar, y solo podemos esperar unos días.
    • necesitaríamos una instancia de base de datos específica para cada desarrollador, o cada máquina, y tener una política para lanzar solo una prueba a la vez. Necesitamos usar Oracle 11 para verificar que nuestras consultas estén bien, eso sería demasiado costoso (licencias de Oracle).
  • limpie cuidadosamente después de cada prueba (los marcos de prueba proporcionan ganchos para ejecutar el código después de una prueba). Eso falla como:
    • es muy costoso mantener una coincidencia perfecta entre el código que doesy la prueba que undoes. Tan pronto como la coincidencia no sea perfecta, los síntomas no son claros, una prueba puede fallar y aparecer unos cientos de líneas más tarde; o una prueba que debería fallar podría pasar erróneamente.
    • siempre hay casos extremos donde la limpieza no es correcta (¿falla la máquina o el software?) Eso deja un estado desconocido para ejecuciones posteriores.
    • cuando una prueba muestra un error, el estado de la base de datos sería una información esencial, pero se perdería (porque limpiamos antes de la próxima prueba, para finalizar el conjunto de pruebas y mostrarle al desarrollador una información completa).

Así que pasamos a una política que sabíamos que era válida:

  • tener pruebas unitarias automáticas para nuestros requisitos :
    • si es necesario, simule los accesos a la base de datos (pero generalmente no necesitamos JMock, podría explicar nuestro diseño si se lo pidieran).
    • las pruebas se ejecutan con una velocidad superior a 100 por segundo
    • Las pruebas son rápidas de escribir, cortas y claras de leer.
    • las pruebas no dependen de ningún estado persistente, por lo que están completamente aisladas entre sí de forma natural (no se necesita un mecanismo complejo)
  • prueba de integración con la base de datos por separado (es decir, solicitud por solicitud, no integrada con la lógica de la aplicación).
    • consideramos automatizar esa tarea, pero parecía demasiado costosa (ver parte anterior de mi publicación)
    • así que guardamos el manual de estas pruebas: se espera que cada desarrollador que modifique una consulta vuelva a probarla en la base de datos (que generalmente es parte de sus pruebas de extremo a extremo a través de la interfaz de usuario).
KLE
fuente
2

Incluso si no tiene "transacciones", en el sentido T-SQL, debe esforzarse por hacer que sus transacciones (en el sentido general del término) sean atómicas. Las pruebas no deben basarse entre sí y deben ser reversibles. Si no tiene ningún alcance oficial de reversión o transacción, entonces puede querer hacer el suyo. Por ejemplo, puede hacer que sus pruebas unitarias realicen una limpieza, donde eliminen todos los registros que se crearon en la prueba.

Morgan Herlocker
fuente
Eso ya ocurre sin transacción (al final, las tablas se vacían ya que siempre comienzo mis pruebas con tablas vacías) pero no resuelve el problema de que se ejecuten varias pruebas al mismo tiempo. Si estoy ejecutando pruebas de integración para la aplicación A y la aplicación B al mismo tiempo y ambas insertan 10 registros en database.table_a, si tengo una prueba para asegurarme de que hay 10 registros en la tabla pero obtengo un resultado de 10, la prueba fallaría aunque esa prueba esté funcionando. Este es el caso que estoy tratando de evitar.
ryanzec
@ryanzec: no lo sigo del todo. Si tiene una prueba para asegurarse de que hay 10 registros en la tabla y obtiene un resultado de 10, entonces parece que la prueba pasará.
Morgan Herlocker
@ryanzec - Creo que podría ver lo que quieres decir. Si la aplicación A requiere 10 registros en la tabla para pasar, pero la aplicación B insertó algunos extra al mismo tiempo, entonces la prueba de A falla. En este caso, deberá escribir sus pruebas de manera diferente. Proporcionaría algún tipo de número de transacción, por lo que no estoy buscando ningún 10 registros en una tabla para una prueba, sino 10 registros con un número de transacción asociado.
Morgan Herlocker
0

Solo haga que cada usuario o cada ejecución anule la base de datos utilizada. Eso es lo que hacemos en el trabajo. Entonces nunca tendrá problemas con 2 pruebas simultáneas que interfieren entre sí.

Cada ejecución de prueba construye la base de datos con migraciones, llena la base de datos con elementos fijos y luego los desarma al final.

Si el DBMS admite transacciones, lo usamos como una optimización para la configuración inicial y el desmontaje. Son opcionales, aunque su prueba puede durar un poco sin ella. Como siempre YYMV.

Sin alboroto, sin muss.

dietbuddha
fuente