¿La prueba de unidad de su SQL está llevando TDD demasiado lejos?
unit-testing (12)
Creo que es un poco excesivo. Supongo que podría hacerlo y colocar las pruebas en una categoría especial para que no se ejecuten en cada compilación, tal vez solo cuando esté registrado y se ejecute en el servidor. Normalmente, con todas las pruebas de unidad no se desean dependencias externas.
Hay un artículo en www.sqlservercentral.com sobre la prueba de unidad de su SQL.
El chico de TDD en mí dijo que bien, podemos probar las cosas de la base de datos.
El arquitecto de sistemas en mí dijo, ¿qué lógica estamos probando? No debe haber ninguna lógica en la base de datos, lo único que debe hacer en la base de datos es seleccionar, actualizar o insertar.
Entonces, si siente la necesidad de probar su SQL por unidad, ¿está siendo realmente exhaustivo, demasiado pragmático, o es un signo de olor de diseño?
Depende de la arquitectura de su base de datos. Si solo tiene tablas y vistas, creo que las pruebas unitarias no son necesarias, ya que todos los errores (o la mayoría) se detectarán en las pruebas unitarias en la aplicación.
Pero si tiene funciones complejas, procedimientos almacenados, desencadenadores, etc., entonces tiene muchos lugares donde puede haber errores y la prueba de la unidad de aplicación no los cubre.
Distinguir entre pruebas / especificaciones de unidad y pruebas / especificaciones de integración .
Si sus clases tienen ambas, entonces está violando un principio sólido: Separación de preocupaciones .
Sus pruebas deben estar claramente definidas entre las pruebas unitarias para las pruebas de las unidades POCO / POJO ignorantes persistentes, como entidades, servicios y pruebas de integración. Que son para probar donde tu aplicación golpea el metal.
Las pruebas de integración deben probar la persistencia, como la implementación de repositorios y unidades de trabajo para su mecanismo de persistencia (RBDMS), Active Directory, Exchange, sistema de archivos y correo electrónico, etc.
Si su caso de uso requiere la prueba completa de un punto de integración, que utiliza un desencadenador, entonces pruebe el comportamiento y no el desencadenante explícitamente. En el futuro, puede elegir no usar un activador y, en su lugar, utilizar un interceptor ORM o AoP.
El arquitecto del sistema es correcto. No debería insertar la lógica de negocios en su base de datos y, por lo tanto, no está haciendo ninguna prueba de unidad.
En la mayoría de los proyectos vivos, la base de datos está en una cierta cantidad de flujo entre los hitos del proyecto. Las tablas y columnas se crean, se caen o se modifican. Las tablas de búsqueda se actualizan. Y podría estar probando contra varias instancias de la base de datos, por lo que es bueno tener alguna validación del estado de los metadatos y los datos en la base de datos, como parte de sus pruebas de regresión.
Hay varios casos donde sugeriría probar una base de datos:
Tablas y vistas: verifique las tablas y vistas que espera que existan. Verifique que estas tablas y vistas contengan las columnas que espera. También puede verificar que las tablas, vistas o columnas que ha introducido en este hito en realidad están ausentes.
Restricciones: Intente ejecutar cambios de datos que deben ser rechazados. Las restricciones deben evitar estos cambios. Puede evitar errores posteriores si detecta casos en los que las restricciones no funcionan.
Disparadores: igual que para las restricciones, y también los disparadores se pueden usar para efectos en cascada, o para transformar valores, etc. Pruebe estas rutas lógicas.
Procedimientos almacenados: apoyo la precaución de no poner demasiada lógica en la base de datos, cuando la lógica se desarrolla, depura y mantiene más fácilmente en la capa de aplicación. Pero hay casos en que hay razones de peso para usar procs almacenados. A menudo, se ve un cuello de botella en el rendimiento solucionado al colocar una lógica compleja en la base de datos. Así que los procesos almacenados no se van por completo, y probarlos es una buena idea.
Datos de arranque: las tablas de búsqueda son un ejemplo de datos que deben estar presentes incluso en una base de datos "vacía". Puede haber otros ejemplos. Prueba de que la base de datos contiene los datos requeridos.
Consultas: Su código de aplicación está enlazado con consultas SQL. Pruébalos para la funcionalidad adecuada y también para el rendimiento. Especialmente el rendimiento, porque la misma consulta puede funcionar bien un día y convertirse en un cuello de botella al día siguiente, ya que el volumen de datos cambia, los índices se desequilibran, etc.
Clases de ORM: al igual que los desencadenantes, las clases de ORM en su aplicación pueden contener lógica para validar, transformar o monitorear las operaciones de la base de datos. Estos deben ser probados.
Es posible que estas pruebas no se llamen con precisión "pruebas unitarias". La prueba de unidad es un tipo específico de prueba en la que cada prueba es independiente de otras pruebas, y usted intenta probar pequeñas unidades de código de forma aislada. Yo diría que probar la base de datos de las formas descritas anteriormente es un ejemplo de prueba de funcionalidad .
Este tema es muy debatido. Si le pregunta a un DBA, pensarán que es lo mejor del mundo que todas sus aplicaciones utilicen procedimientos almacenados predefinidos. Sin embargo, esos días están llegando a su fin, con Hibernate y LINQ ganando popularidad, realmente PUEDE usar la base de datos como un depósito de información y hacer que su capa de acceso a datos procese todas las solicitudes. Creo que LINQ puede hacer todo por usted en MS SQL, excepto las búsquedas de texto completo. En cuanto a la diferencia de rendimiento entre SPROCs y LINQ, es despreciable. Mi voto no es un código en la base de datos, todo el código en la capa de acceso a datos de yoru, y lo estoy probando.
Estoy de acuerdo con el arquitecto del sistema, demasiada lógica de negocios está llegando a las bases de datos en estos días.
No hago TDD directamente en mi base de datos, pero hay muchas oportunidades donde es válido colocar "lógica" en la base de datos. Restricciones, valores predeterminados (sí, también sé que es una restricción), desencadenadores, etc. A menudo, esta es la mejor manera de implementar cierta lógica de negocios Y asegurar la consistencia de la base de datos. La mayoría de las veces puedo convencerme de la corrección con algunas pruebas manuales y dejarlo así, pero puedo ver dónde alguien podría querer hacer TDD con esto.
EDITAR :
Por ejemplo, usaré un valor predeterminado en la inserción y un desencadenante en la actualización para establecer los campos de "última actualización" en la inserción / actualización. En LINQ configuraré la columna como un valor generado automáticamente y lo haré de solo lectura. Esto es más simple, para mí, que agregar un controlador de eventos PropertyChanged para asegurarse de que cada vez que se cambia un campo en la entidad, la hora de la última actualización también se modifique. ¿Lo pruebo? Claro, pero manualmente y después del hecho, aunque prefiero TDD para la mayoría de las cosas.
No sé por qué, cuando llegamos a la capa db, todas las buenas prácticas deberían ir por la ventana. Si hay lógica en esta capa es doblemente importante. Hay una gran herramienta construida sobre Fitnesse llamada dbfit que parece eliminar todo el dolor de la unidad al probar el dblayer. Si te interesa deberías echar un vistazo.
Siempre que la cláusula WHERE no esté vacía, se debe probar.
Aquí utilizamos la API de criterios de NHibernate para consultar la base de datos. Aún así, ponemos pruebas unitarias simples para salvaguardar la capa de acceso a datos. Considera esto:
public IList<Book> GetBorrowedBooks(User user);
Puede parecer tonto en primer lugar. Pero para una situación tan simple, estamos tratando con al menos 3 objetos modelo: Libro, Usuario, Pedir prestado y quizás Devolver. Cualquier intento de modificar cualquiera de las 3 (o más) clases puede romper el código.
Cual es el costo Escribir las pruebas en este ejemplo toma menos de 20 minutos, supongo. Con la ayuda de Categoría en NUnit, las pruebas de la unidad de acceso a datos se pueden configurar para que se ejecuten de noche, mientras que otras pruebas se ejecutan en cada confirmación. Las pruebas de unidades de acceso lento a los datos no dañan, y son salvavidas.
Tu SQL contiene lógica. Por ejemplo, la condición booleana se verifica en la cláusula "WHERE". ¿Puedes pensar en alguna forma en que el SQL podría estar equivocado? Si es así, ¿tendría sentido probar el SQL para asegurarse de que estos errores no están presentes?
(Por ejemplo, un programador tonto, como yo, podría escribir accidentalmente "WHILE" en lugar de "WHERE" en mi comentario anterior ... como lo hice. Pero luego lo corregí. ¿Entonces, dónde están mis pruebas de ?!? -)
DbFit es una buena herramienta para la prueba unitaria de la base de datos. Creo que es aconsejable usar TDD con SQL porque, después de todo, es un lenguaje declarativo con bifurcación condicional, funciones agregadas, etc. Sin las pruebas implementadas, ¿cómo puede estar seguro de que está obteniendo el resultado deseado?