Si las pruebas unitarias solo cubren software 'funcional'

9

Estamos utilizando StructureMap en un nuevo proyecto de desarrollo de software. Uno de los miembros del equipo ha implementado una prueba de unidad que básicamente prueba la configuración del contenedor de StructureMap . Hace esto haciendo lo siguiente;

  • Cuenta el número de instancias de ensamblajes configurados para clases en el espacio de nombres de nuestra aplicación.
  • Define instancias esperadas a nivel de clase
  • Afirma que las instancias esperadas coinciden con el total de instancias encontradas.
  • Afirma que las instancias esperadas coinciden con las definidas en la prueba

Un ejemplo de esto es;

var repositories = container.GetAllInstances<IEnvironmentRepository>();
Assert.AreEqual(1, repositories .Count());
foundInstances = foundInstances + repositories .Count();

También tenemos 'pruebas unitarias' para la siguiente clase;

public MyClass(IEnvironmentRepository environmentRepository)
        {

        }

En estas pruebas, nos burlamos de IEnvironmentRepository, por lo que no lo inyectaríamos desde el contenedor como sucedería en el sistema en vivo.

Un colega ignoró la prueba de la unidad en la configuración del mapa de estructura con un comentario en la línea de "La prueba de la unidad solo prueba su propia configuración". Obviamente, este era el propósito de la prueba y, en mi opinión, es perfectamente válido. Le pedí al tipo que ignoró la prueba que eliminara la configuración del mapa de estructura para IEnvironmentRepository(con la prueba aún ignorada) y ejecutara el conjunto de pruebas de la unidad completa, todos pasaron. Luego ejecutamos la aplicación y se cayó porque la configuración del contenedor ahora no era válida. En mi opinión, esto demostró el valor de la prueba, mi colega aún no estaba de acuerdo. Simplemente declaró que no deberíamos probar la configuración, pero considero que esto está dentro del alcance de una prueba unitaria.

Entonces una serie de preguntas;

  • ¿Es una prueba de unidad válida? Estamos probando la configuración de nuestro contenedor, no que el mapa de estructura funcione (pero puedo ver la superposición)
  • Si no, ¿cómo puede validar la configuración sin probarla? ¿Cómo puede evitar que alguien elimine accidentalmente una línea de código requerida y la registre?
  • ¿Debería la MyClassprueba unitaria resolver la instancia del IEnvironmentRepositorycontenedor y pasar esto?
ChrisBint
fuente
10
9 de cada 10 desacuerdos sobre las pruebas surgen del hecho de que los marcos admiten pruebas automatizadas en todas sus formas, y las personas quieren entrar en la semántica de si una prueba automatizada en particular es una prueba de unidad buena y adecuada o no. La prueba que describe suena como el tipo de prueba de prueba no unitaria que puede ser muy útil para tener y automatizar (y ejecutar en el registro), simplemente no lo llame una prueba unitaria. Pregunte si su colega dormiría mejor por la noche si la prueba viviera en su propia función / carpeta que estaba claramente separada.
Jeroen Mostert
2
Esa es también mi opinión, probablemente útil, y aunque no es estrictamente una prueba unitaria, agrega valor y esto ha sido probado. Su respuesta fue que las otras pruebas unitarias habrían recogido esto, pero en mi opinión, si se escribieran como pruebas unitarias estrictas, se burlarían de las dependencias y, por lo tanto, nunca sabría si la configuración era válida hasta que la usara.
ChrisBint
44
Su colega tiene un punto en el que dice que no debe probar la configuración, ya que la configuración genuina que realmente puede variar según la implementación no puede / no debe probarse: ¿quién puede decir que "rojo" está mal y "azul" no? La prueba estaría estrechamente acoplada a una configuración. Pero la configuración que está vinculada a los artefactos de código es una excepción, ya que no varía y claramente hay formas de equivocarse. Idealmente, tendrías dicha configuración generada en tiempo de compilación a partir de metadatos DRY, pero cuando esto no sea factible, una prueba como esta agrega valor. Mejor eso que un error de implementación evitable.
Jeroen Mostert
2
Lo que está describiendo no prueba una unidad, prueba la configuración de un software de terceros. Es increíblemente útil tener pruebas que prueben estas cosas, pero son pruebas de integración, no pruebas unitarias, y la desconexión puede ser la raíz del desacuerdo.
Phoshi
3
@ChrisBint Dios mío, no, yo mismo he escrito un montón de pruebas de contenedores. Tienen mucho valor, simplemente no son pruebas unitarias. Está bien, las pruebas de integración son extremadamente valiosas para detectar cosas que las pruebas unitarias no pueden .
Phoshi

Respuestas:

13

Esta es una prueba automatizada perfectamente válida para tener. Los llamo "pruebas de arquitectura", ya que verifican la solidez de los componentes esqueléticos de su base de código.

¿El contenedor IoC puede resolver y componer todos los árboles de objetos en la aplicación? ¿Puede el mapeador automático mapear entre todos sus objetos registrados sin fallar? ¿La capa central en una arquitectura de cebolla no hace referencia a nada externo?

Estas pruebas pueden ahorrarle mucho tiempo cuando un error de configuración se cuela, señalando al culpable exacto. Los buenos marcos te darán mensajes de error muy precisos sobre lo que está mal y los recibirás tan pronto como ejecutes las pruebas (idealmente, continuamente) en lugar de enterrar en el fondo un seguimiento de la pila de tiempo de ejecución si tienes suerte.

Ya sean pruebas unitarias ... probablemente no, pero aún operan en la memoria en su mayor parte y funcionan bastante rápido. Por otra parte, no sé, no es como si hubiera una definición universalmente aceptada de prueba unitaria.

guillaume31
fuente
Irónicamente, esto es más o menos como se lo expliqué a mi colega e incluso con la validación (eliminar una de las instancias del contenedor y ejecutar la aplicación) todavía no veía ningún valor. Entiendo que todos tienen su propia opinión, y expresé la mía;) Me encanta el término "prueba de arquitectura", ¡voy a robar eso!
ChrisBint
6

El problema con una prueba como esta que prueba las partes internas del programa, en lugar de un requisito del mismo. Es que la prueba puede fallar incluso si el programa funciona como se requiere.

En su caso, cada vez que cambia la configuración del contenedor, tal vez tenga una nueva dependencia que necesita inyectarse, interrumpe su prueba.

Además, si agrega el requisito de dependencia adicional, pero olvide agregarlo al contenedor y cambiar la prueba del contenedor. todo pasará, pero su programa se bloqueará.

Una mejor prueba automatizada sería iniciar el programa y ver si falla.

Debe detectar estos tipos de error en las pruebas de integración o UI, incluso si caen en las pruebas unitarias.

Dicho esto, la creciente complejidad de la configuración del contenedor es una molestia. Quizás algunas pruebas "malas" valen la pena.

Ewan
fuente
1

Código de prueba de pruebas unitarias. Cualquier cosa fuera de esto son "otras" pruebas automatizadas: llámelo como quiera. Parece que estás probando la configuración aquí. Si la configuración puede cambiar según el entorno, no pertenece a una prueba unitaria. Considere agregar un atributo de prueba para indicar que la prueba es de un tipo diferente a las otras pruebas.

Robbie Dee
fuente
La configuración es estática, no está controlada por el entorno, todas las clases que existen en la configuración se usarán en todos los entornos de la misma manera. Sí, el número de instancias que podrían estar en la configuración debe coincidir con el número de instancias en la configuración, que es parte de la prueba. Como mostró mi ejemplo, eliminar IEnvironmentRepository permitió que las otras pruebas unitarias pasen. La prueba del contenedor específico hubiera fallado en 2 Asserts; 1: el número total de posibles declaraciones de instancia no coincidía y 2: el número específico de instancias de IEnvironmentRepository no coincidiría.
ChrisBint
1
La exactitud del contenedor está definida por el codificador. El hecho de que tanto el código bajo prueba como la prueba en sí misma tengan que cambiar para cada modificación establece inmediatamente que suenen las alarmas. DI es un medio para un fin y no el fin en sí mismo. Es perfectamente posible escribir código en un estilo DI sin el mapa de estructura ergo, no es una prueba de unidad de buena fe en mi opinión. El contenedor, por supuesto, necesita ser probado, pero la eficacia de hacerlo con pruebas automatizadas parecería ser algo discutible con la información limitada proporcionada aquí.
Robbie Dee
2
Las pruebas unitarias demoraron 10 minutos en producirse. El despliegue podría llevar más de una hora.
ChrisBint
1
Dada parte de la prueba unitaria valida específicamente la existencia de una sola línea en la configuración, no estoy seguro de cómo eso no podría haber estado más aislado. El recuento general con el que podría estar de acuerdo.
ChrisBint
1
Entonces podría haber algo de kilometraje en ellos, una decisión de juicio para usted realmente. Pero deben separarse físicamente o mediante algún atributo.
Robbie Dee
0

La responsabilidad del contenedor de inyección de dependencia es pegar diferentes módulos en una aplicación de trabajo .

Si escribe pruebas automatizadas para su aplicación, debe tener pocas pruebas de "integración (o aceptación) que ejecuten pruebas" de principio a fin ", que probarán toda la tubería de su aplicación, que todos los módulos involucrados en un caso de prueba en particular estén pegados correctamente .

Por lo tanto, esas pruebas de integración fallarán si el contenedor de inyección de dependencia no se configuró correctamente. Lo que hace que las pruebas unitarias para el contenedor en sí sean inútiles, porque la prueba de integración debe mostrar posibles errores en la configuración del contenedor.

No es necesario que cubra todos los casos de prueba posibles en las pruebas de integración, solo un caso de prueba por función que cubre el camino completo desde la interfaz de usuario a la base de datos.

Si los casos de prueba de integración no cubren la creación de instancias de alguna dependencia particular, simplemente agregue dicha.

Con las pruebas de integración, puede cambiar libremente los contenedores sin tener que volver a escribir pruebas unitarias para su configuración.

Fabio
fuente
0

OMI, las respuestas son:

  1. ¿Es una prueba de unidad válida? Estamos probando la configuración de nuestro contenedor, no que el mapa de estructura funcione (pero puedo ver la superposición)

    • Es una prueba unitaria válida para el mapa de estructura , no para su proyecto, porque una prueba unitaria prueba un código específico, burlándose de todas las dependencias si es necesario para probar la lógica implementada. La lógica de configuración se implementa dentro del mapa de estructura, por lo tanto, esta biblioteca debe estar bien probada y debe contener pruebas unitarias como esta que mencionó, y más: debe contener cientos de pruebas como esta, simulando dinámicamente varias configuraciones en tiempo de ejecución y pruebas para ver si contenedor se comporta como debería.
  2. Si no, ¿cómo puede validar la configuración sin probarla? ¿Cómo puede evitar que alguien elimine accidentalmente una línea de código requerida y la registre?

    • Puede probar la configuración manualmente en el entorno necesario, y también puede crear una automatización para esto (prueba automatizada), que prueba la configuración específica que necesita (no es necesario burlarse de cosas en tiempo de ejecución).
  3. ¿Debería la prueba de unidad MyClass resolver la instancia de IEnvironmentRepository del contenedor y pasar esto?

    • No, esta es una prueba de unidad perfecta, porque se burla de la dependencia y prueba la lógica de MyClass de forma aislada.
Emerson Cardoso
fuente
-1

UnitTest verifica el comportamiento deseado de una unidad en separación .

Esto significa que cualquier tipo de configuración no está dentro del alcance de UnitTests .

No obstante, debe tener pruebas automatizadas para sus configuraciones, pero estas no son UnitTests ...

Timothy Truckle
fuente
¿De dónde sacas la definición de Unidades?
ChrisBint
Me gusta el de Roy Osherove en The Art Of Unittesting : A Unit es cualquier fragmento de código (de producción) que tiene la misma razón para cambiar. En mi mundo, esto generalmente varía desde una sola clase hasta tres o cinco ...
Timothy Truckle
Este es el código de producción que se está probando.
ChrisBint
Solo quería distraer a los nitpickers , sin esperar que funcione al revés también ...; o)
Timothy Truckle