¿Cuántas burlas es "justo?"

10

Titulé la pregunta en broma porque estoy seguro de que "depende", pero tengo algunas preguntas específicas.

Al trabajar en un software que tiene muchas capas profundas de dependencia, mi equipo se ha acostumbrado a utilizar la burla de manera bastante extensa para separar cada módulo de código de las dependencias que se encuentran debajo.

Por lo tanto, me sorprendió que Roy Osherove sugiriera en este video que deberías usar burlarte solo como el 5% del tiempo. Supongo que estamos sentados en algún lugar entre 70-90%. He visto otra guía similar de vez en cuando también.

Debería definir lo que considero que son dos categorías de "pruebas de integración" que son tan distintas que realmente deberían recibir nombres diferentes: 1) Pruebas en proceso que integran múltiples módulos de código y 2) Pruebas fuera de proceso que hablan a bases de datos, sistemas de archivos, servicios web, etc. Es el tipo # 1 lo que me preocupa, las pruebas que integran múltiples módulos de código en proceso.

Gran parte de la orientación comunitaria que he leído sugiere que debería preferir una gran cantidad de pruebas unitarias aisladas y de grano fino y una pequeña cantidad de pruebas integrales de integración de extremo a extremo, porque las pruebas unitarias le brindan información precisa sobre exactamente dónde Es posible que se hayan creado regresiones, pero las pruebas groseras, que son difíciles de configurar, en realidad verifican una mayor funcionalidad de extremo a extremo del sistema.

Dado esto, parece necesario hacer un uso bastante frecuente de la burla para aislar estas unidades de código separadas.

Dado un modelo de objeto de la siguiente manera:

ingrese la descripción de la imagen aquí

... También considere que la profundidad de dependencia de nuestra aplicación es mucho más profunda de lo que podría caber en esta imagen, de modo que hay varias capas N entre la capa 2-4 y la capa 5-13.

Si quiero probar alguna decisión lógica simple que se toma en la unidad n. ° 1, y si cada dependencia se inyecta al constructor en el módulo de código que depende de él, de modo que, por ejemplo, 2, 3 y 4 se inyectan al constructor en el módulo 1 en la imagen, preferiría inyectar simulacros de 2, 3 y 4 en 1.

De lo contrario, necesitaría construir instancias concretas de 2, 3 y 4. Esto puede ser más difícil que simplemente escribir algo más. A menudo 2, 3 y 4 tendrán requisitos de constructor que pueden ser difíciles de satisfacer y de acuerdo con el gráfico (y de acuerdo con la realidad de nuestro proyecto), necesitaré construir instancias concretas de N a 13 para satisfacer a los constructores de 2, 3 y 4.

Esta situación se vuelve más desafiante cuando necesito que 2, 3 o 4 se comporten de cierta manera para poder probar la simple decisión lógica en el n. ° 1. Es posible que necesite comprender y "razonar mentalmente" sobre todo el gráfico / árbol de objetos de una vez para que 2, 3 o 4 se comporten de la manera necesaria. A menudo parece mucho más fácil hacer myMockOfModule2.Setup (x => x.GoLeftOrRight ()). Returns (new Right ()); para probar que el módulo 1 responde como se esperaba cuando el módulo 2 le dice que vaya a la derecha.

Si tuviera que probar instancias concretas de 2 ... N ... 13 en conjunto, las configuraciones de prueba serían muy grandes y en su mayoría duplicadas. Las fallas en las pruebas podrían no hacer un muy buen trabajo al identificar las ubicaciones de las fallas de regresión. Las pruebas no serían independientes ( otro enlace de soporte ).

Por supuesto, a menudo es razonable realizar pruebas de la capa inferior basadas en el estado, en lugar de la interacción, ya que esos módulos rara vez tienen más dependencia. Pero parece que la burla es casi necesaria, por definición, para aislar cualquier módulo que esté por encima del más bajo.

Dado todo esto, ¿alguien puede decirme lo que podría estar perdiendo? ¿Está nuestro equipo sobreutilizando simulacros? ¿O tal vez hay alguna suposición en la guía de pruebas de unidad típica de que las capas de dependencia en la mayoría de las aplicaciones serán lo suficientemente superficiales como para que sea razonable probar todos los módulos de código integrados juntos (haciendo nuestro caso "especial")? O quizás de manera diferente, ¿nuestro equipo no está limitando adecuadamente nuestros contextos limitados?

Ardave
fuente
Me parece que su aplicación podría beneficiarse de un acoplamiento más flexible. en.wikipedia.org/wiki/Loose_coupling
Robert Harvey
1
Or is there perhaps some assumption in typical unit testing guidance that the layers of dependency in most applications will be shallow enough that it is indeed reasonable to test all of the code modules integrated together (making our case "special")? <- Esto.
Robert Harvey
También vale la pena señalar: el propósito de las pruebas de regresión (especialmente las pruebas de integración) es demostrar que su software aún funciona, no necesariamente para identificar dónde se rompe. Puede hacerlo con la solución de problemas, solucionar el problema y luego cubrir la rotura específica con pruebas unitarias adicionales.
Robert Harvey
Debería haber sido más claro en la publicación original, al decir que el módulo 1 solo conoce I2, I3 e I4. El módulo 2 solo conoce I5, I6 e I7. Es solo el objetivo cuestionable de probar sin usar simulacros que proporcionaría un concreto 2, 3 y 4 a 1, lo que lleva a los desafíos que describí. De lo contrario, terminamos usando simulacros mucho más del 5% del tiempo.
Ardave
Bromeé acerca de que nuestro caso era "especial" después de leer una publicación de blog sobre muchos equipos desafiando convenciones valiosas porque sentían incorrectamente que su situación era "especial". Pero si este es realmente nuestro caso, esto explicaría la disparidad entre parte de la orientación comunitaria que he leído y algunas de las experiencias reales de mi equipo. (el 5% vs 70-90%)
llegó el

Respuestas:

4

¿Está nuestro equipo sobreutilizando simulacros?

No a primera vista.

Si tiene 1..13 módulos, cada uno debe tener sus propias pruebas unitarias, y todas las dependencias (no triviales, no confiables) deben reemplazarse por versiones de prueba. Eso puede significar simulacros, pero algunas personas son pedantes con los nombres, por lo que falsificaciones, cuñas, objetos nulos ... algunas personas se refieren a todas las implementaciones de prueba como "simulacros". Esta podría ser la fuente de la confusión sobre cuánto es "correcto".

Personalmente, llamo a todos los objetos de prueba "simulacros" porque a menudo no es útil distinguir entre la variedad de ellos. Mientras mantengan mis pruebas unitarias rápidas, aisladas y resistentes ... No me importa cómo se llamen.

Telastyn
fuente
Entonces, me pregunto si hay alguna guía general para cuándo es mejor probar los módulos de código de forma aislada en lugar de probar más de un módulo de código, integrados juntos. Parece que tan pronto como integro cualquiera de los dos módulos que de otro modo podría haber aislado, me abro a una serie de problemas indeseables: falta de identificación de causas de regresión / fallas de pruebas múltiples para una regresión única, configuraciones de prueba excesivas, etc. Tengo mi propio sentido intuitivo ("escuchar las pruebas") pero esto me ha dejado en el nivel de simulación del 70-90%.
Ardave
1
@nono - En mi experiencia, deberías probar todo de forma aislada, por las razones que mencionas. Las únicas cosas que no prueba unitariamente son cosas que no puede hacer porque van en contra de algún sistema de archivos u otro recurso externo directamente ( algo tiene que hacer eso después de todo).
Telastyn
Después de masticar esto durante unos días, la suya parece ser la mejor explicación posible: si uno usara la definición estricta de "simulacro" como un tipo de prueba doble utilizada para la interacción retrospectiva / verificación de comportamiento, en oposición a una prueba ficticia doble o un doble de prueba que está configurado de antemano para simular cierto comportamiento, entonces sí, podría ver la liquidación al nivel del 5%.
Ardave