¿Cómo hacer que una prueba dependa de los resultados de otra prueba?

13

Digamos que hay una clase de utilidad que proporciona algunos métodos estáticos comunes utilizados en muchas partes de su código por muchas otras clases.

¿Cómo diseñaría sus pruebas unitarias para los consumidores de la utilidad para que sus pruebas fallen si alguna de las pruebas de la utilidad no pasa? ¿Puede hacerlo o debe comprobarlo usted mismo si las pruebas de clase de utilidad son todas verdes?

Por ejemplo, tengo una utilidad de división de mensajes que es utilizada (o más bien su salida) por un analizador de mensajes. Me gustaría asegurarme de que el separador de mensajes funciona correctamente antes de que se pruebe el analizador de mensajes.

He escrito pruebas para ambos, pero ¿hay alguna forma de vincularlos y hacer que una prueba dependa del resultado de alguna otra prueba?

No pude encontrar una etiqueta adecuada para esto, pero estoy usando el motor de prueba de unidad de Visual Studio.

t3chb0t
fuente
1
Entiendo que el analizador no funciona correctamente si la utilidad falla. Pero si ejecuta sus pruebas verá que las pruebas de utilidad fallan. ¿Por qué quieres que otras pruebas también fallen?
Eugen Martynov
1
Nunca he oído hablar de alguien que quiera esto antes. ¿Qué estás tratando de lograr?
Esben Skov Pedersen
Utilizo algunas de las funciones de la utilidad para preparar datos para las pruebas del analizador, por lo que me gustaría estar absolutamente seguro de que la utilidad funciona correctamente antes de probar cualquier otra cosa.
t3chb0t
3
@ t3chb0t Si desea asegurarse de que su utilidad funcione, debe escribir pruebas unitarias para verificar su utilidad. Las pruebas unitarias deben ser atómicas. Solo quieres que prueben un solo componente.
maple_shaft

Respuestas:

11

No tiene sentido asegurarse de que cada defecto en su sistema dispare exactamente una prueba.

Un conjunto de pruebas tiene un trabajo: verificar que no haya defectos conocidos. Si hay un defecto, no importa si una prueba falla o 10. Si te acostumbras a la falla de tu conjunto de pruebas, si intentas medir qué tan "malo" es tu programa contando las pruebas que fallan, no estás usando la prueba de regresión de la manera correcta. El conjunto de pruebas debe pasar todas las pruebas antes de publicar el código.

La única razón válida para omitir las pruebas es si prueban una funcionalidad incompleta y toman una cantidad excesiva de tiempo que podría aprovechar mejor al implementar lo que se supone que deben probar. (Eso es solo un problema si no practicas un desarrollo estricto basado en pruebas, pero esa es una opción válida, después de todo).

De lo contrario, no se moleste en tratar de convertir su conjunto de pruebas en un indicador que le diga exactamente qué está mal. Nunca será exacto, y no se supone que lo sea. Se supone que te protege de cometer el mismo error dos veces, eso es todo.

Kilian Foth
fuente
8

Depende de sus herramientas, pero probablemente no pueda (y no debería)

Algunos marcos de pruebas unitarias (tome PHPUnit por ejemplo) le permiten 'encadenar' pruebas para que una prueba fallida en un nivel no ejecute otras pruebas. Sin embargo, dudo que esto solucione su problema para esta situación.

No permitir que la ejecución de la orden de prueba garantizada obligue a las pruebas a aislarse entre sí y generalmente se considera algo bueno. Imagine lo que sucedería si las pruebas no solo se ejecutasen por sí mismas, sino que también proporcionaran datos para que otras pruebas funcionen ...

Puede colocar estos métodos de utilidad en una solución separada o categoría de prueba. Asegúrese de que se destaquen visualmente en su corredor de prueba, o que esta categoría se ejecute primero y no ejecute ninguna otra prueba si las pruebas en esta categoría fallan *. Cuando una de estas pruebas falla, es probable que la falla ocurra en cascada en todas las pruebas y usted debe ayudar a asegurarse de que quien ejecute las pruebas sepa que debe comenzar primero corrigiendo estas pruebas fallidas. Lo mismo ocurre con las pruebas unitarias y las pruebas de integración. Si una prueba de unidad falla, caerá en cascada y causará todo tipo de caos en las pruebas de integración. Todo el mundo sabe que cuando las pruebas de la Unidad Y la Integración fallan, comienzas revisando las Pruebas de la Unidad ...

* Con un script de compilación o prueba automatizado, primero podría ejecutar esta categoría, verificar el resultado y solo ejecutar las otras pruebas si pasa este primer lote de pruebas.

JDT
fuente
5

Intente mantener su unidad de prueba atómica. Recuerde que el código de prueba automatizado también es parte de su base de código, pero en sí no está bajo prueba, así que manténgalo lo más simple y obvio posible.

Para responder su pregunta más directamente, no hay garantía del orden de ejecución de las pruebas, por lo que tampoco hay forma de garantizar que su prueba de utilidad tenga éxito antes de que se ejecuten sus otras pruebas. Por lo tanto, no hay una forma garantizada de lograr lo que desea sin dejar de tener un único conjunto de pruebas para todas sus pruebas.

Puede mover las utilidades a su propia solución y agregar una referencia a su dll resultante en su proyecto actual.

Torácico
fuente
4

En resumen, no desea utilizar herramientas / técnicas para hacer otras cosas que no sean para lo que están destinadas.

Lo que intenta hacer parece una preocupación que podría resolverse fácilmente con una práctica de CI (integración continua).

De esta manera, puede mantener sus pruebas atómicas como ya se sugirió y dejar que el CI se encargue de verificar sus pruebas.

Si alguna prueba falla, puede configurarla para que no permita que su código se publique.

AvetisG
fuente
4

Tengo la costumbre de tratar siempre de diferenciar el código de nivel de aplicación del código de nivel de marco, por lo que me he encontrado con el problema que está describiendo con bastante frecuencia: generalmente desea que se pruebe todo el código de nivel de marco antes de que cualquier código de nivel de aplicación comience a probarse. . Además, incluso dentro del código de nivel de marco, tienden a existir algunos módulos de marco fundamentales que son utilizados por todos los demás módulos de marco, y si algo falla en los fundamentos, realmente no tiene sentido probar nada más.

Desafortunadamente, los proveedores de marcos de prueba tienden a tener ideas algo rígidas sobre cómo se deben usar sus creaciones, y son bastante protectores de esas ideas, mientras que las personas que usan sus marcos tienden a aceptar el uso previsto sin cuestionarse. Esto es problemático porque sofoca la experimentación y la innovación. No conozco a todos los demás, pero preferiría tener la libertad de intentar hacer algo de una manera extraña, y ver por mí mismo si los resultados son mejores o peores que los establecidos, en lugar de no tener la libertad de hacer las cosas a mi manera en primer lugar.

Entonces, en mi opinión, las dependencias de prueba serían algo increíble, y en lugar de eso, la capacidad de especificar el orden en que se ejecutarán las pruebas sería la mejor opción.

La única forma en que he encontrado para abordar el tema de ordenar pruebas es nombrando cuidadosamente, para explotar la tendencia de los marcos de prueba a ejecutar pruebas en orden alfabético.

No sé cómo funciona esto en Visual Studio, porque todavía tengo que hacer algo que implique pruebas exhaustivas con C #, pero en el lado de Java del mundo funciona de la siguiente manera: en la carpeta de origen de un proyecto, generalmente tenemos dos subcarpetas, uno llamado "main", que contiene el código de producción, y otro llamado "test", que contiene el código de prueba. En "principal" tenemos una jerarquía de carpetas que corresponde exactamente a la jerarquía de paquetes de nuestro código fuente. Los paquetes Java corresponden aproximadamente a espacios de nombres de C #. C # no requiere que haga coincidir la jerarquía de carpetas con la jerarquía de espacio de nombres, pero es recomendable hacerlo.

Ahora, lo que la gente suele hacer en el mundo de Java es que debajo de la carpeta "prueba" reflejan la jerarquía de carpetas que se encuentra debajo de la carpeta "principal", de modo que cada prueba reside exactamente en el mismo paquete que la clase que prueba. La razón detrás de esto es que, con bastante frecuencia, la clase de prueba necesita acceder a miembros privados del paquete de la clase bajo prueba, por lo que la clase de prueba debe estar en el mismo paquete que la clase bajo prueba. En el lado C # del mundo no existe la visibilidad local del espacio de nombres, por lo que no hay razón para reflejar las jerarquías de carpetas, pero creo que los programadores de C # siguen más o menos la misma disciplina en la estructuración de sus carpetas.

En cualquier caso, me parece que esta idea de permitir que las clases de prueba tengan acceso a los miembros del paquete local de las clases bajo prueba mal orientadas, porque tiendo a probar interfaces, no implementaciones. Por lo tanto, la jerarquía de carpetas de mis pruebas no tiene que reflejar la jerarquía de carpetas de mi código de producción.

Entonces, lo que hago es nombrar las carpetas (es decir, los paquetes) de mis pruebas de la siguiente manera:

t001_SomeSubsystem
t002_SomeOtherSubsystem
t003_AndYetAnotherSubsystem
...

Esto garantiza que todas las pruebas para "SomeSubsystem" se ejecutarán antes de todas las pruebas para "SomeOtherSubsystem", que a su vez se ejecutarán antes de todas las pruebas para "AndYetAnotherSubsystem", y así sucesivamente.

Dentro de una carpeta, los archivos de prueba individuales se nombran de la siguiente manera:

T001_ThisTest.java
T002_ThatTest.java
T003_TheOtherTest.java

Por supuesto, es de gran ayuda que los IDE modernos tengan potentes capacidades de refactorización que le permiten cambiar el nombre de paquetes completos (y todos los subpaquetes, y todo el código que los hace referencia) con solo un par de clics y pulsaciones de teclas.

Mike Nakis
fuente
2
I don't know about everyone else, but I would prefer to have the freedom to try to do something in an odd way, and see for myself whether the results are better or worse++ compañero. No sé si me gusta la solución, pero me gusta la actitud que has mostrado allí.
RubberDuck el