¿Es una mala práctica hacer cumplir una orden de ejecución para pruebas unitarias?

84

Estoy escribiendo pruebas para un proyecto que consta de múltiples submódulos. Cada caso de prueba que he escrito se ejecuta de forma independiente y borro todos los datos entre pruebas.

Aunque las pruebas se ejecutan de forma independiente, estoy considerando aplicar una orden de ejecución, ya que algunos casos requieren más de un submódulo. Por ejemplo, un submódulo está generando datos y otro está ejecutando consultas sobre los datos. Si el submódulo que genera los datos contiene un error, la prueba para el submódulo de consulta también fallará, incluso si el submódulo en sí funciona bien.

No puedo trabajar con datos ficticios, ya que la funcionalidad principal que estoy probando es la conexión a un servidor remoto de caja negra, que solo obtiene los datos del primer submódulo.

En este caso, ¿está bien hacer cumplir una orden de ejecución para las pruebas o es una mala práctica? Siento que hay un olor en esta configuración, pero no puedo encontrar una mejor manera de evitarlo.

editar: la pregunta es ¿Cómo estructurar pruebas donde una prueba es la configuración de otra prueba? como la prueba "anterior" no es una configuración, pero prueba el código que realiza la configuración.

Ali Rasim Kocal
fuente
123
Si está probando la conexión a un servidor remoto, entonces, por definición, no son pruebas unitarias.
Telastyn
99
La primera respuesta me confundió aquí porque en tu título dijiste "¿Es una mala práctica?" y en el resumen de tu pregunta escribiste "¿está bien?" ¡Cualquiera que responda sí o no va a confundir uno de esos!
Liath
8
Parece que está creando un conjunto de pruebas de integración. Incluso para esa prueba no debería basarse en otras pruebas.
Low Flying Pelican
17
Si el orden importa, entonces probablemente lo estás haciendo mal.
Mark Rogers

Respuestas:

236

No puedo trabajar con datos ficticios, ya que la funcionalidad principal que estoy probando es la conexión a un servidor remoto de caja negra, que solo obtiene los datos del primer submódulo.

Esta es la parte clave para mí. Puede hablar de "pruebas unitarias" y de que "se ejecutan independientemente unas de otras", pero todas suenan como si fueran dependientes de este servidor remoto y dependientes del "primer submódulo". Entonces todo suena estrechamente acoplado y depende del estado externo. Como tal, de hecho está escribiendo pruebas de integración. Hacer que esas pruebas se ejecuten en un orden específico es bastante normal, ya que dependen en gran medida de factores externos. Una ejecución de prueba ordenada, con la opción de salir temprano de la ejecución de prueba si las cosas salen mal, es perfectamente aceptable para las pruebas de integración.

Pero también valdría la pena echar un vistazo a la estructura de su aplicación. Ser capaz de simular el primer submódulo y el servidor externo podría permitirle escribir verdaderas pruebas unitarias para todos los demás submódulos.

David Arno
fuente
14
Sin mencionar que algunas pruebas tienen que verificar específicamente que el comportamiento esperado se produce cuando el servidor remoto no está disponible.
Alexander
2
O, tal vez, de hecho tiene la intención de escribir pruebas de integración, y así burlarse de los datos no va a lograr lo que está tratando de lograr con estas pruebas.
Guy Schalnat el
10
El problema es más probable que Junit tenga "unidad" en su nombre.
Thorbjørn Ravn Andersen
77
@ ThorbjørnRavnAndersen Exactamente. Las personas escriben naturalmente pruebas de integración, en lugar de pruebas unitarias, porque las pruebas de integración son mucho más útiles y mucho menos difíciles de escribir que las pruebas unitarias "reales". Pero debido a que los marcos de prueba populares fueron nombrados por el concepto de pruebas unitarias, el término ha sido cooptado y ha llegado a significar "cualquier prueba automatizada" en el lenguaje moderno.
Mason Wheeler
1
@MasonWheeler O incluso cooptado por gerentes no técnicos para significar pruebas de aceptación.
TKK
32

Sí, es una mala práctica.

Generalmente, una prueba unitaria está destinada a probar una sola unidad de código (por ejemplo, una única función basada en un estado conocido).

Cuando desee probar una cadena de eventos que podrían ocurrir en la naturaleza, desea un estilo de prueba diferente, como una prueba de integración. Esto es aún más cierto si depende de un servicio de terceros.

Para realizar pruebas unitarias como esta, debe encontrar una manera de inyectar los datos ficticios, por ejemplo, implementando una interfaz de servicio de datos que refleje la solicitud web pero devuelva datos conocidos de un archivo de datos ficticios local.

Pablo
fuente
8
Convenido. Creo que esta confusión surge del hecho de que muchas personas tienen la idea de que las pruebas de integración tienen que ser de extremo a extremo, y usan "prueba unitaria" para referirse a cualquier prueba que solo pruebe una capa .
Autofagia
8
@autophage: Definitivamente de acuerdo con esto. De hecho, estoy tan de acuerdo con eso que regularmente me encuentro cayendo en la misma trampa a pesar de estar de acuerdo en que es una trampa 😂
Lightness Races in Orbit
16

La orden de ejecución forzada que propone solo tiene sentido si también cancela la ejecución de la prueba después del primer error.

Anular la ejecución de la prueba en la primera falla significa que cada ejecución de prueba puede descubrir un solo problema y no puede encontrar nuevos problemas hasta que se hayan solucionado todos los problemas anteriores. Si la primera prueba que se ejecuta encuentra un problema que tarda un mes en solucionarse, entonces durante ese mes, efectivamente, no se ejecutarán pruebas.

Si no cancela la ejecución de la prueba en el primer fallo, la orden de ejecución forzada no le compra nada porque cada prueba fallida debe investigarse de todos modos. Aunque solo sea para confirmar que la prueba en el submódulo de consulta falla debido a la falla que también se identificó en el submódulo generador de datos.

El mejor consejo que puedo dar es escribir las pruebas de tal manera que sea fácil identificar cuándo una falla en una dependencia está causando que la prueba falle.

Bart van Ingen Schenau
fuente
7

El olor al que te refieres es la aplicación de un conjunto incorrecto de restricciones y reglas a tus pruebas.

Las pruebas unitarias a menudo se confunden con "pruebas automatizadas" o "pruebas automatizadas de un programador".

Las pruebas unitarias deben ser pequeñas, independientes y rápidas.

Algunas personas leen incorrectamente esto como "las pruebas automatizadas escritas por un programador deben ser pequeñas, independientes y rápidas" . Pero simplemente significa que si sus pruebas no son pequeñas, independientes y rápidas, no son Pruebas unitarias y, por lo tanto, algunas de las reglas para las Pruebas unitarias no deberían, no pueden o no deben aplicarse para sus pruebas. Un ejemplo trivial: debe ejecutar sus Pruebas de unidad después de cada compilación, lo que no debe hacer para las pruebas automatizadas que no son rápidas.

Si bien sus pruebas no son pruebas unitarias significa que puede omitir una regla y se le permite tener cierta interdependencia entre las pruebas, también descubrió que hay otras reglas que puede haber omitido y que tendrá que volver a introducir, algo para el alcance de otra pregunta .

Peter
fuente
6

Como se señaló anteriormente, lo que está ejecutando parece ser una prueba de integración, sin embargo, afirma que:

Por ejemplo, un submódulo está generando datos y otro está ejecutando consultas sobre los datos. Si el submódulo que genera los datos contiene un error, la prueba para el submódulo de consulta también fallará, incluso si el submódulo en sí funciona bien.

Y este puede ser un buen lugar para comenzar a refactorizar. El módulo que ejecuta consultas en los datos no debe depender de una implementación concreta del primer módulo (generador de datos). En cambio, sería mejor inyectar una interfaz que contenga los métodos para llegar a esos datos y esto se puede burlar para probar las consultas.

p.ej

Si usted tiene:

class Queries {

    int GetTheNumber() {
        var dataModule = new Submodule1();
        var data = dataModule.GetData();
        return ... run some query on data
    }
}

En cambio prefiero:

interface DataModule {
    Data GetData();
}


class Queries {

    IDataModule _dataModule;

    ctor(IDataModule dataModule) {
       _dataModule = dataModule;
    }

    int GetTheNumber() {
        var data = _dataModule.GetData();
        return ... run some query on data
    }
}

Esto elimina la dependencia de las consultas en su fuente de datos y le permite configurar pruebas unitarias fácilmente repetibles para escenarios particulares.

Arrozal
fuente
6

Las otras respuestas mencionan que ordenar pruebas es malo (lo cual es cierto la mayoría de las veces), pero hay una buena razón para imponer el orden en la ejecución de la prueba: asegurarse de que sus pruebas lentas (es decir, pruebas de integración) se ejecuten después de sus pruebas más rápidas (pruebas que no dependen de otros recursos externos). Esto garantiza que ejecute más pruebas más rápido, lo que puede acelerar el ciclo de retroalimentación.

Mike Holler
fuente
2
Me inclinaría más a investigar por qué cierta prueba de unidad se ejecuta lentamente en lugar de hacer cumplir una orden. Se supone que las pruebas unitarias son rápidas.
Robbie Dee
El OP dice que no puede trabajar con datos ficticios para algunas de estas pruebas. Eso significa un golpe de base de datos de algún tipo, ralentizando todas las pruebas (incluso algunas pruebas unitarias verdaderas que deberían ejecutarse rápidamente de forma natural). Si tiene otras pruebas que no requieren visitas a la base de datos, se ejecutarán un orden de magnitud más rápido que cualquier cosa que requiera visitas al disco o la red.
Mike Holler
2
Ambos tienen razón, creo; Robbie tiene razón en que las pruebas unitarias deben ser pequeñas y rápidas y aisladas de las dependencias, por lo que el orden no debería importar y el orden aleatorio a menudo fomenta un mejor diseño al hacer cumplir esa independencia; y Mike tiene razón en que ejecutar pruebas más rápidas primero es muy, muy bueno para las pruebas de integración . Como en las respuestas anteriores, parte del problema es la terminología de las pruebas de unidad frente a las de integración.
WillC
@MikeHoller Entonces no son pruebas unitarias. Realmente no debería haber confusión en cuanto a qué son las pruebas unitarias .
Robbie Dee
@RobbieDee Simplemente estaba usando la terminología que estaba usando el OP. Entiendo que estas no son pruebas unitarias verdaderas. Si quieres pelear por la terminología, tráela con OP. (de ahí que aclare con "pruebas unitarias verdaderas" en mi comentario anterior ")
Mike Holler