database unit-testing transactions xtunit

database - Bases de datos de pruebas unitarias



unit-testing transactions (9)

El verano pasado desarrollé una aplicación CRUD de servidor ASP.NET/SQL básica, y la prueba de unidad fue uno de los requisitos. Me encontré con algunos problemas cuando intenté contra la base de datos. A mi entender, las pruebas unitarias deberían ser:

  • apátrida
  • independientes entre sí
  • repetible con los mismos resultados, es decir, sin cambios persistentes

Estos requisitos parecen estar en desacuerdo entre ellos cuando se desarrollan para una base de datos. Por ejemplo, no puedo probar Insertar () sin asegurarme de que las filas que se van a insertar aún no están allí, por lo tanto, necesito llamar a Eliminar () primero. Pero, ¿y si no están ya allí? Entonces necesitaría llamar primero a la función Exists ().

Mi solución final involucró funciones de configuración muy grandes (¡asqueroso!) Y un caso de prueba vacío que se ejecutaría primero e indicaría que la configuración se ejecutó sin problemas. Esto se está sacrificando por la independencia de las pruebas mientras se mantiene su apatridia.

Otra solución que encontré es ajustar las llamadas a función en una transacción que se puede revertir fácilmente, como XtUnit de Roy Osherove . Este trabajo, pero implica otra biblioteca, otra dependencia, y parece una solución demasiado pesada para el problema en cuestión.

Entonces, ¿qué ha hecho la comunidad SO cuando se enfrenta a esta situación?

tgmdbm dijo:

Por lo general, utiliza su marco de prueba de unidad automatizado favorito para realizar pruebas de integración, por lo que algunas personas se confunden, pero no siguen las mismas reglas. Se le permite involucrar la implementación concreta de muchas de sus clases (porque han sido probadas en unidades). Está probando cómo interactúan sus clases concretas entre sí y con la base de datos .

Entonces, si leo esto correctamente, realmente no hay forma de probar de manera efectiva la capa de acceso a datos. O, ¿una "prueba unitaria" de una capa de acceso a datos implicará probar, por ejemplo, los comandos SQL / generados por las clases, independientemente de la interacción real con la base de datos?


Expliqué una técnica que he estado usando para esta misma situación aquí .

La idea básica es ejercitar cada método en su DAL - afirmar sus resultados - y cuando cada prueba esté completa, desmantelar para que su base de datos esté limpia (no hay datos basura / de prueba).

El único problema que quizás no encuentres "excelente" es que normalmente realizo una prueba CRUD completa (no pura desde la perspectiva de la prueba unitaria) pero esta prueba de integración te permite ver tu código de mapeo CRUD + en acción. De esta forma, si se rompe, lo sabrá antes de iniciar la aplicación (me ahorra muchísimo trabajo cuando intento ir rápido)


He tenido la misma pregunta y he llegado a las mismas conclusiones básicas que las otras respuestas aquí: No moleste en probar la unidad de la capa de comunicación DB real, pero si desea probar las funciones de su modelo (para asegurarse de que están tirando datos correctamente, formateándolo adecuadamente, etc.), utilice algún tipo de fuente de datos ficticia y pruebas de configuración para verificar los datos que se recuperan.

También me parece que la definición escueta de pruebas unitarias es inadecuada para muchas actividades de desarrollo web. Pero esta página describe algunos modelos de pruebas unitarias más "avanzadas" y puede ayudar a inspirar algunas ideas para aplicar pruebas unitarias en diversas situaciones:

Patrones de prueba de unidad


La solución habitual para las dependencias externas en las pruebas unitarias es usar objetos falsos, es decir, bibliotecas que imitan el comportamiento de las reales contra las que está probando. Esto no siempre es sencillo, y algunas veces requiere un poco de ingenio, pero hay varias bibliotecas de simulacros buenas (freeware) para .Net si no desea "rodar las suyas". Dos vienen a la mente de inmediato:

Rhino Mocks es uno que tiene una muy buena reputación.

NMock es otro.

También hay muchas bibliotecas de simulacros comerciales disponibles. Parte de escribir buenas pruebas unitarias es en realidad diseñar su código para ellas, por ejemplo, al usar interfaces donde tiene sentido, para que pueda "burlarse" de un objeto dependiente al implementar una versión "falsa" de su interfaz que, no obstante, se comporta de manera predecible, para propósitos de prueba.

En los simulacros de bases de datos, esto significa "burlarse" de su propia capa de acceso a bases de datos con objetos que devuelven objetos de tablas, filas o conjuntos de datos inventados para las pruebas de su unidad.

Donde trabajo, normalmente hacemos nuestras propias librerías simuladas desde cero, pero eso no significa que tengas que hacerlo.


No hay una forma real de probar una base de datos aparte de afirmar que las tablas existen, contienen las columnas esperadas y tienen las restricciones apropiadas. Pero eso generalmente no vale la pena hacerlo.

Normalmente no prueba la base de datos en una unidad . Por lo general, implica la base de datos en las pruebas de integración .

Por lo general, utiliza su marco de prueba de unidad automatizado favorito para realizar pruebas de integración, por lo que algunas personas se confunden, pero no siguen las mismas reglas. Se le permite involucrar la implementación concreta de muchas de sus clases (porque han sido probadas en unidades). Está probando cómo interactúan sus clases concretas entre sí y con la base de datos.


Probar juntos la capa de datos y la base de datos deja pocas sorpresas para más adelante en el proyecto. Pero las pruebas en la base de datos tienen sus problemas, el principal es que está probando en contra del estado compartido por muchas pruebas. Si inserta una línea en la base de datos en una prueba, la siguiente prueba también puede ver esa línea.
Lo que necesita es una forma de deshacer los cambios que realiza en la base de datos.
La clase TransactionScope es lo suficientemente inteligente como para manejar transacciones muy complicadas, así como también transacciones anidadas en las que su código en llamadas de prueba se compromete en su propia transacción local. Aquí hay un código simple que muestra lo fácil que es agregar capacidad de reversión a sus pruebas:

[TestFixture] public class TrannsactionScopeTests { private TransactionScope trans = null; [SetUp] public void SetUp() { trans = new TransactionScope(TransactionScopeOption.Required); } [TearDown] public void TearDown() { trans.Dispose(); } [Test] public void TestServicedSameTransaction() { MySimpleClass c = new MySimpleClass(); long id = c.InsertCategoryStandard("whatever"); long id2 = c.InsertCategoryStandard("whatever"); Console.WriteLine("Got id of " + id); Console.WriteLine("Got id of " + id2); Assert.AreNotEqual(id, id2); } }


Sí, debes refactorizar tu código para acceder a Repositorios y Servicios que acceden a la base de datos y luego puedes simular o resguardar esos objetos para que el objeto bajo prueba nunca toque la base de datos. ¡Esto es mucho más rápido que almacenar el estado de la base de datos y restablecerla después de cada prueba!

Recomiendo mucho a Moq como tu marco de burla. He usado Rhino Mocks y NMock. Moq era tan simple y resolvió todos los problemas que tuve con los otros frameworks.


Si está utilizando LINQ to SQL como ORM, puede generar la base de datos sobre la marcha (siempre que tenga suficiente acceso desde la cuenta utilizada para la prueba de la unidad). Ver http://www.aaron-powell.com/blog.aspx?id=1125


DBunit

Puede usar esta herramienta para exportar el estado de una base de datos en un momento determinado, y luego, cuando esté realizando la prueba unitaria, puede volver a su estado anterior automáticamente al comienzo de las pruebas. Lo usamos con bastante frecuencia donde trabajo.


Lo que debe hacer es ejecutar sus pruebas desde una copia en blanco de la base de datos que genera desde un script. Puede ejecutar sus pruebas y luego analizar los datos para asegurarse de que tiene exactamente lo que debería después de ejecutar sus pruebas. Luego, simplemente borre la base de datos, ya que es un regalo. Todo esto puede automatizarse y puede considerarse una acción atómica.