Ortogonalidad de pruebas unitarias versus concisión de pruebas unitarias

14

Estoy escribiendo pruebas unitarias para un sistema de dirección para un videojuego. El sistema tiene varios comportamientos (evite esta área debido a la razón A, evite esta área debido a la razón B, y cada uno agrega un poco de contexto a un mapa de la región. Una función separada analiza el mapa y produce el movimiento deseado.

Tengo problemas para decidir cómo escribir las pruebas unitarias para los comportamientos. Como sugiere TDD, solo estoy interesado en cómo los comportamientos afectan el movimiento deseado. Por ejemplo, evitar-por-razón-A debería resultar en un movimiento fuera de la mala posición sugerida. En realidad, no me importa cómo o por qué el comportamiento agrega contexto al mapa, solo que el movimiento deseado está lejos de la posición.

Entonces, mis pruebas para cada comportamiento configuran el comportamiento, lo hacen escribir en el mapa, luego ejecuta la función de análisis del mapa para calcular el movimiento deseado. Si ese movimiento satisface mis especificaciones, entonces estoy feliz.

Sin embargo, ahora mis pruebas dependen de que los comportamientos funcionen correctamente y de que la función de análisis de mapas funcione correctamente. Si la función de análisis falla, obtendría cientos de pruebas fallidas en lugar de un par. Muchas guías de redacción de exámenes sugieren que esta es una mala idea.

Sin embargo, si pruebo directamente contra la salida de los comportamientos burlándome del mapa, ¿entonces seguramente me acoplaré demasiado a la implementación? Si puedo obtener el mismo movimiento deseado del mapa usando un comportamiento ligeramente diferente, entonces las pruebas aún deberían pasar.

Así que ahora estoy sufriendo disonancia cognitiva. ¿Cuál es la mejor manera de estructurar estas pruebas?

tenpn
fuente
... no estoy seguro de que haya una respuesta mágica. Básicamente pruebas cosas que pueden romperse. Por lo tanto, idealmente, de alguna manera mágicamente sería capaz de decir qué pruebas de bajo nivel ya estarían suficientemente cubiertas por las pruebas de alto nivel que no necesitarían sus propias pruebas unitarias de nivel inferior. Otra forma de verlo es: desde el momento en que una prueba falla hasta el momento en que un desarrollador soluciona el problema, ¿cuánto tiempo pasará? Quieres que este tiempo sea bajo. Si no hiciera unidades, sino solo pruebas funcionales perfectas (cobertura completa de alto nivel), ese tiempo aún sería demasiado alto. Intenta usar eso como una guía heurística.
Calphool

Respuestas:

10

En el mundo ideal, de hecho tendría un conjunto de pruebas unitarias perfectamente ortogonales, todas en el mismo nivel de abstracción.

En el mundo real, generalmente tiene pruebas en muchos niveles diferentes de la aplicación, por lo que a menudo las pruebas de nivel superior ejercen la funcionalidad que ya ha sido probada por pruebas de nivel inferior dedicadas. (Muchas personas prefieren llamar pruebas de subsistema / integración de pruebas de nivel superior en lugar de pruebas unitarias; sin embargo, aún pueden ejecutarse en el mismo marco de pruebas unitarias, por lo que desde el punto de vista técnico no hay mucha diferencia).

No creo que esto sea algo malo. El punto es que su código sea probado de la mejor manera que se adapte a su proyecto y su situación, no adherirse a "la forma ideal".

Péter Török
fuente
Supongo que el punto principal del autor era si debería usar stubs / simulacros o implementaciones reales para estas pruebas de nivel superior
SiberianGuy
2

Este tipo de prueba es la razón por la cual se inventaron las simulaciones. La idea principal: escribe un simulacro para su objeto (mapa, comportamiento, carácter, ...), luego escribe pruebas utilizando ese simulacro en lugar del objeto real. La gente a veces llama trozos simulados, y creo que hay otras palabras para ambos.

En su caso, escribiría un simulacro para el mapa cada vez que necesite probar los comportamientos, y otros simulacros para los comportamientos cada vez que desee probar el mapa. Idealmente, sus simulacros serían mucho más simples que los comportamientos reales de los que se está burlando, incorporando solo métodos o variables que realmente necesita para esa prueba. Es posible que tenga que escribir un simulacro diferente para cada prueba, o puede reutilizar algunos simulacros. De todos modos, deberían ser adecuados para la prueba y no deberían tratar de ser lo más parecidos posible al comportamiento real.

Si incluyó algunos ejemplos de mapas o comportamientos, tal vez alguien podría proporcionar ejemplos de los simulacros que podría escribir. No a mí, ya que nunca he programado un videojuego más avanzado que Pong, e incluso entonces seguía un libro, pero tal vez alguien bien versado en las pruebas unitarias y el desarrollo de juegos.

Trysis
fuente
0

Creo que intentas probar cosas que tienen un nivel mucho más alto que una unidad.

La mayoría de las pruebas de comportamiento requerirían algo de inteligencia para saber si se comportó correctamente. Esto no se puede hacer fácilmente usando pruebas automatizadas.

enone
fuente
Por eso mencioné la concisión. Puedo escribir fácilmente pequeñas pruebas unitarias para probar líneas de código individuales dentro de un comportamiento, y lo he hecho, pero depende de la lectura de la salida de la función de análisis de mapas. Ninguna de mis pruebas de comportamiento es grande, cada una de ellas prueba solo una pieza de funcionalidad del comportamiento.
2011