Cómo TDD para que se devuelvan los resultados correctos


12

Estoy comenzando un nuevo proyecto y estoy intentando utilizar TDD para impulsar el diseño. He estado presionando durante años, y finalmente obtuve la aprobación para dedicar más tiempo a este proyecto para usarlo mientras aprendo cómo hacerlo correctamente.

Este es un nuevo módulo, para vincularlo a un sistema existente. En la actualidad, todo el acceso a los datos se realiza a través de servicios web, que en su mayor parte son solo una envoltura delgada sobre los procedimientos almacenados de la base de datos.

Un requisito es que para una tienda determinada, devuelvo todas las órdenes de compra que se consideran válidas para esta aplicación. Una orden de compra se considera válida si su fecha de envío cae dentro de un rango determinado desde la fecha de apertura de las tiendas (esto es para nuevas tiendas).

Ahora, no puedo poner esta lógica en el código de la aplicación, ya que no voy a recuperar un millón de pedidos de compra solo para obtener la docena que se aplica a esta tienda dada la restricción anterior.

Estaba pensando, podría pasar el rango de fechas a un proceso GetValidPOs y hacer que use esos valores para devolver los PO válidos. Pero, ¿qué pasa si agregamos otro requisito a lo que se considera una orden de compra válida?

¿Y cómo pruebo esto y verifico que sigue funcionando? No estamos utilizando un ORM, y es poco probable que suceda. Y no puedo llamar al DB en mi prueba.

Estoy atascado.

Mi otro pensamiento es tener algunos simulacros que devuelven datos válidos, otros que devuelven algunos datos incorrectos y hacer que el repositorio local arroje una excepción si se producen datos incorrectos y compruebe que la excepción se genera si el proceso GetValidPOs devuelve datos no válidos (o el simulacro utilizado en las pruebas).

¿Esto tiene sentido? ¿O hay un mejor camino?

ACTUALIZACIÓN: Parece que puedo usar EF. Ahora solo necesito descubrir cómo usarlo y hacerlo comprobable, al mismo tiempo que puedo confiar en los procedimientos almacenados y la dificultad de tener datos dispersos en varias bases de datos.


Por curiosidad, ¿por qué no puede seleccionar solo los pedidos válidos con una declaración SQL simple? (Esta pregunta o la respuesta no implica una solución.)
Scarfridge

Respuestas:


7

Esta es una desventaja importante de los procedimientos almacenados en la era de TDD. Tienen algunas ventajas reales, incluso ahora, pero por definición cualquier prueba que ejercita un proceso almacenado no es una prueba unitaria; Es una prueba de integración en el mejor de los casos.

La solución habitual, suponiendo que la arquitectura no puede cambiar para usar un ORM en su lugar, es no poner estas pruebas en el conjunto de pruebas unitarias; en su lugar, ponga las pruebas en un paquete de integración. Todavía puede ejecutar la prueba cada vez que desee verificar que funciona, pero debido a que el costo inherente a la configuración de la prueba (inicializar una base de datos con los datos de prueba adecuados) es alto y toca los recursos, es posible que el agente de prueba de unidad de su robot de construcción no tener acceso a, no debería estar en el conjunto de pruebas unitarias.

Todavía puede probar el código de la unidad que requiere los datos, abstrayendo cualquier cosa que no pueda probar la unidad (clases ADO.NET) en una clase DAO que luego pueda burlarse. Luego, puede verificar que las llamadas esperadas se realicen consumiendo código y reproduciendo comportamientos del mundo real (como no encontrar resultados) que permitan probar varios casos de uso. Sin embargo, la configuración real de SqlCommand para llamar al proceso almacenado es prácticamente lo último que puede probar unitariamente, separando la creación de comandos de la ejecución de comandos y burlándose del ejecutor de comandos. Si esto suena como una gran separación de preocupaciones, puede ser; recuerde, "no hay problema que no pueda ser resuelto por otra capa de indirección, excepto por tener demasiadas capas de indirección". En algún momento debes decir "suficiente; simplemente no puedo hacer una prueba unitaria de esto, nosotros '

Otras opciones:

  • Pruebe el proceso almacenado utilizando una instancia de DBMS "de corta duración" como SQLite. Por lo general, es más fácil hacer esto cuando se usa un ORM, pero la prueba se puede hacer "en memoria" (o con un archivo de base de datos preestablecido incluido con el conjunto de pruebas). Todavía no es una prueba unitaria, pero se puede ejecutar con un alto grado de aislamiento (el DBMS es parte del proceso de ejecución y no es algo a lo que se conecta de forma remota que puede estar en el medio del conjunto de pruebas conflictivas de otra persona). La desventaja es que los cambios en el proceso almacenado pueden ocurrir en la producción sin que la prueba refleje el cambio, por lo que debe ser disciplinado para asegurarse de que el cambio se realice primero en un entorno de prueba.

  • Considere actualizar a un ORM. Un ORM con un proveedor de Linq (prácticamente todos los de uso común tienen uno) le permitiría definir la consulta como una declaración de Linq; esa declaración se puede dar a un Repositorio simulado que tiene una colección en memoria de datos de prueba para aplicarlo. Por lo tanto, puede verificar que la consulta sea correcta sin siquiera tocar la base de datos (aún debe ejecutar la consulta en un entorno de integración, para probar que el proveedor de Linq puede digerir correctamente la consulta).


2
-1 porque TDD! = Prueba unitaria. Perfectamente bien para incluir pruebas de nivel de integración al hacer TDD.
Steven A. Lowe

Las pruebas unitarias son un subconjunto del desarrollo impulsado por pruebas. En un desarrollo impulsado por pruebas, crearía un esqueleto ambulante de su sistema y luego ejecutaría pruebas unitarias, de integración y funcionales en ese sistema. Sus pruebas de integración, unidad o aceptación fallan, luego las hace pasar y escribe más pruebas.
CodeART

1
Entiendo todo eso, los dos. ¿Dónde dije que tener que ser una prueba de integración significaba que no podías TDD? Mi punto era que un procedimiento almacenado no se puede probar de forma aislada, que es algo que desea hacer en la mayor cantidad de su base de código que pueda. En cambio, probar los SP requiere pruebas de integración más complejas y de mayor duración; Si bien aún son mejores que las pruebas manuales, un conjunto de pruebas con mucha integración puede tardar horas en ejecutarse y puede tener un efecto perjudicial en los esfuerzos de CI.
KeithS

Las pruebas de SP a menudo también requieren un conjunto de datos específico en la base de datos de prueba; El código para obtener la base de datos en el estado adecuado para lograr los resultados esperados es a menudo más LoC y varias veces más largo que el código que realmente está ejerciendo. Esto agrava aún más la complejidad de tiempo del conjunto de pruebas, y a menudo la configuración debe repetirse para cada prueba individual (y probablemente debería haber varias para cada SP, para probar que se cumple cada requisito funcional de la consulta).
KeithS

Los procedimientos almacenados se pueden probar de forma aislada. ¿De qué otra forma serían validados? Para Transact SQL hay tSQLt ( tsqlt.org )
kevin cline

4

Mi consejo es dividir y conquistar . Olvídese de la base de datos y la persistencia por el momento y concéntrese en probar implementaciones falsas de sus repositorios u objetos de acceso a datos.

Ahora, no puedo poner esta lógica en el código de la aplicación, ya que no voy a recuperar un millón de pedidos de compra solo para obtener la docena que se aplica a esta tienda dada la restricción anterior.

Me burlaría del repositorio que devuelve las órdenes de compra. Crea un simulacro con veinte órdenes de compra impares.

Estaba pensando, podría pasar el rango de fechas a un proceso GetValidPOs y hacer que use esos valores para devolver los PO válidos. Pero, ¿qué pasa si agregamos otro requisito a lo que se considera una orden de compra válida?

Aplique una llamada a GetValidPOs para que llame a su simulación, en lugar del procedimiento de base de datos.

¿Y cómo pruebo esto y verifico que sigue funcionando? No estamos utilizando un ORM, y es poco probable que suceda. Y no puedo llamar al DB en mi prueba.

Necesita una prueba unitaria para asegurarse de que los datos correctos se devuelven de un simulacro.

También necesita una prueba de integración para asegurarse de que se devuelven los datos correctos de una base de datos. La prueba de integración requeriría cierta configuración y limpieza. Por ejemplo, antes de ejecutar la prueba de integración, siembra tu base de datos ejecutando un script. Verifique que su script haya funcionado. Consulte la base de datos llamando a sus procedimientos almacenados. Verifique que sus resultados sean correctos. Limpia la base de datos.

Mi otro pensamiento es tener algunos simulacros que devuelven datos válidos, otros que devuelven algunos datos incorrectos y hacer que el repositorio local arroje una excepción si se producen datos incorrectos y compruebe que la excepción se genera si el proceso GetValidPOs devuelve datos no válidos (o el simulacro utilizado en las pruebas).

Como ya dije, necesita un simulacro que devuelva al menos algunos datos que puede consultar.

Cuando consulta datos desea asegurarse de que su sistema pueda manejar las excepciones con gracia. Por lo tanto, se burla del comportamiento para que arroje excepciones en ciertos escenarios. Luego escribe pruebas para asegurarse de que su sistema pueda manejar estas excepciones con gracia.


Eso es lo que estoy tratando de hacer. Simplemente tener dificultades para escribir una implementación real que funcione igual que el simulacro, ya que nuestro acceso a datos no es propicio para usar un ORM. La mayoría de los datos que necesito están en varios sistemas, y se supone que se debe acceder a ellos a través de servicios web ... incluso cuando se actualizan.
CaffGeek

0

Del mismo modo que la prueba unitaria de Java o Javascript significa escribir pruebas unitarias utilizando el lenguaje Java para Java, y la prueba unitaria de funciones Javascript con Javascript, escribir pruebas automatizadas para guiarlo a escribir procedimientos almacenados significa que la biblioteca de pruebas unitarias que está buscando se basa en procedimientos

Dicho de otra manera, use procedimientos almacenados para probar los procedimientos almacenados porque:

  • Como se está desarrollando en el lenguaje de procedimiento, debe tener la habilidad de escribir sus exámenes en el lenguaje de procedimiento.
  • escribir pruebas en su lenguaje de procedimiento aumentará su habilidad en el lenguaje de procedimiento, lo que a su vez ayudará al desarrollo de su producto
  • tendrá acceso directo a todas las herramientas que proporciona su base de datos y también puede usar esas herramientas para que sus pruebas unitarias sean lo más simples posible
  • las pruebas unitarias que se almacenan en la misma base de datos que los procedimientos que están probando serán rápidas (lo más parecido a las pruebas unitarias como las velocidades) porque no está cruzando los límites del sistema

Al igual que TDD en un idioma OO, desea que su prueba unitaria configure solo una fila de datos para probar lo que necesita para el procedimiento (minimalismo, solo tiene lo que necesita su prueba simple). El resultado de esto es que tendrá varias pruebas unitarias simples para cada procedimiento almacenado. Estas pruebas simples serán más fáciles de mantener que tener pruebas complicadas que dependen de un gran conjunto de datos que no se asigna fácilmente a lo que la prueba realmente necesita.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.