¿Cómo diseñar el software de un juego de modo que sea fácil realizar una prueba unitaria?

25

¿Es práctico usar un marco de prueba como JUnit en una situación de desarrollo de juegos? ¿Qué tipo de consideraciones de diseño puedes seguir para que tu juego sea más comprobable? ¿Qué partes de un juego pueden / deben probarse y qué partes deben / deben dejarse a prueba en humanos?

Por ejemplo, si el ciclo del juego está encapsulado en una función, parece que sería terriblemente difícil de probar. Me gusta refactorizar una función de "actualización" que toma un tiempo delta y avanza la lógica del juego; Esto permite algunos trucos interesantes, como la capacidad de ralentizar el juego al alimentarlo con deltas falsos y más lentos.

Ricket
fuente
66
¿Por qué desperdiciar las pruebas de la unidad de escritura de horas hombre cuando tienes un ejército prácticamente interminable de mano de obra esclava para hacer pruebas de juego por ti? Bromeo, bromeo ...;)
Mike Strobel
1
En realidad no necesitas niño, ¡ese es un muy buen punto! No pensé en eso, pero los juegos son el tipo de software más fácil para que otras personas lo prueben. Supongo que eso compensa un poco la dificultad de las pruebas de juego automatizadas (unidad o no).
Ricket
3
Las pruebas unitarias no se tratan necesariamente de asegurarse de que su aplicación funcione correctamente como un todo (es decir, pruebas funcionales). Se trata más de asegurarse de que los cambios futuros no rompan la funcionalidad existente. Claro, un usuario puede ver cuándo la nave espacial está al revés, pero cuando el algoritmo de ruta está desactivado en .001, los efectos pueden no ser evidentes hasta que el juego se juegue durante 200 horas. Ese es el tipo de cosas que las pruebas unitarias pueden detectar antes de que el código salga por la puerta.
Wade Williams el
1
Los juegos son el software más fácil para que los usuarios jueguen , pero jugar! = Pruebas . Obtener buenos informes de errores es bastante difícil. Más allá de eso, rastrear en qué parte del código se produce un error y verificar que los nuevos cambios no rompan el código existente, ambos se benefician dramáticamente de las pruebas automatizadas.
munificente

Respuestas:

25

Uno de los principios de TDD es que permite que TDD en algunos casos influya en su diseño. Escribes una prueba para el sistema, luego escribes el código para pasar esa prueba, mantienes las dependencias lo más superficial posible.

Para mí, solo hay dos cosas que no pruebo como parte de las pruebas unitarias:

Primero, no pruebo los elementos visuales y cómo se ven las cosas. Lo pruebo y el objeto estará en el lugar correcto después de actualizarse, que una cámara eliminará un objeto fuera de sus límites, que las transformaciones (al menos las que se realizan fuera de los sombreadores) se realizan correctamente antes de ser entregadas al motor gráfico , pero una vez que llega al sistema de gráficos, trazo la línea. No me gusta tratar de burlarme de cosas como DirectX.

En segundo lugar, realmente no pruebo la función principal del bucle del juego. Compruebo que cada sistema funcionará cuando pase un delta razonable, y que los sistemas funcionen juntos correctamente cuando lo necesiten. Luego solo actualizo cada sistema con el delta correcto en el bucle del juego. En realidad, podría tener una prueba para mostrar que cada sistema recibió una llamada con el delta correcto, pero en muchos casos encuentro esa exageración (a menos que esté haciendo una lógica compleja para obtener su delta, entonces no es una exageración).

Jeff
fuente
66
El primer párrafo es clave. El hecho es que TDD requiere que diseñe su código mejor de lo que lo haría sin él, simplemente para permitir la prueba. Por supuesto, muchos piensan que son expertos en diseño, pero, por supuesto, los que están más impresionados con sus propias habilidades suelen ser los que tienen más que aprender ...
dash-tom-bang
2
No estaría de acuerdo con que el código esté necesariamente diseñado mejor como resultado. He visto muchas abominaciones en las que una interfaz previamente limpia tuvo que romperse y romperse la encapsulación para inyectar dependencias comprobables.
Kylotan
44
Me parece que sucede más en los casos en que las pruebas se escriben para clases preexistentes en lugar de al revés.
Jeff
@Kylotan que probablemente sea más un síntoma de diseño incorrecto / diseño de prueba deficiente. Refactorice y use simulacros para hacer pruebas limpias, no piratee su código en un estado peor; Eso es lo opuesto a lo que se supone que debe suceder.
timoxley
2
La encapsulación es algo bueno, pero lo más importante es entender POR QUÉ es algo bueno. Si solo lo usa para ocultar dependencias y una interfaz grande y fea, casi grita que su diseño probablemente no sea muy agradable. La encapsulación no se trata principalmente de ocultar el código y las dependencias feas, se trata principalmente de hacer que su código sea más fácil de entender y permitir que el consumidor obtenga menos caminos a seguir cuando intenta razonar sobre el código.
Martin Odhelius
19

Noel Llopis ha cubierto las pruebas unitarias en la medida que creo que está buscando:

Con respecto a probar todo el ciclo del juego, existe otra técnica para evitar regresiones funcionales en su código. La idea es hacer que su juego se juegue a través de un sistema de repetición (es decir, primero registrar las entradas del jugador y luego reproducirlas en una ejecución diferente). Durante la reproducción, genera una instantánea del estado de cada objeto para cada cuadro. Esta colección de estados puede llamarse la "imagen dorada". Al refactorizar su código, ejecuta la reproducción nuevamente y compara el estado de cada cuadro con el estado de la imagen dorada. Si difiere, la prueba ha fallado.

Si bien los beneficios de esta técnica son obvios, hay algunas cosas con las que debe tener cuidado:

  • Su juego debe ser determinista, por lo que debe tener cuidado con cosas como, por ejemplo, según el reloj de su sistema, los eventos del sistema / red, los generadores de números aleatorios que no se determinan de manera determinista. etc.
  • Los cambios de contenido después de que se generó la imagen dorada pueden causar diferencias legítimas con su imagen dorada. No hay forma de evitar esto: al cambiar su contenido, necesita regenerar su imagen dorada.
jpaver
fuente
+1 También vale la pena señalar que el valor de este enfoque está directamente relacionado con la longitud de la "imagen dorada": si no es lo suficientemente largo, puede perderse fácilmente errores; si es demasiado largo, tendrás que esperar mucho. Es importante intentar que el juego se ejecute quizás un orden de magnitud más rápido en este modo, que en circunstancias normales de tiempo de ejecución, para reducir el tiempo libre. ¡Saltar el renderizado puede ayudar!
Ingeniero
9

Estoy de acuerdo con los comentarios de Jeff y jpaver. También quería agregar que la adopción de un modelo de componentes para su arquitectura aumenta enormemente su capacidad de prueba. Con un modelo de componente, cada componente debe realizar una sola unidad de trabajo y debe poder probarse de forma aislada (o con objetos simulados limitados).

Del mismo modo, las otras partes del juego que dependen de entidades solo deberían depender de un subconjunto de componentes para funcionar. En la práctica, esto significa que generalmente puede burlarse de algunos componentes falsos para facilitar la prueba de estas áreas o simplemente puede componer entidades parciales para fines de prueba. por ejemplo, omite los componentes de renderizado y entrada porque no son necesarios para probar la física.

Finalmente, ignoraría los consejos sobre el rendimiento que recibe de algunas personas en la comunidad del juego con respecto a las interfaces. Escriba bien el código con las interfaces y si se encuentra con problemas de rendimiento en el futuro, puede fácilmente perfilar, identificar y refactorizar para resolver el problema. No me convencieron las preocupaciones de Noel sobre el impacto en el rendimiento de las interfaces o la complejidad de la sobrecarga que agregaron.

Dicho esto, no te excedas tratando de probar cada pequeña cosa de forma independiente. Como la mayoría de las cosas, las pruebas y el diseño tienen que ver con lograr el equilibrio adecuado.

Alex Schearer
fuente
Tenga en cuenta que, aunque no parece ser nuevo en SX, las respuestas no están ordenadas y decir algo como "los dos comentarios anteriores" seguramente se desvalorizará rápidamente, ya que actualmente es un voto por delante de uno. de las otras dos respuestas en este momento. :)
Ricket
Gracias, no pensé en eso mientras comentaba. Desde entonces he actualizado el comentario para elegir los comentarios individuales.
Alex Schearer
3

Pensé que agregaría una segunda respuesta en respuesta al comentario del OP de que el usuario podría reemplazar las pruebas unitarias. Creo que eso es completamente incorrecto ya que el propósito de las pruebas unitarias no es garantizar la calidad. Si desea realizar pruebas para garantizar la calidad de su programa, probablemente debería investigar las pruebas de escenarios o invertir en un excelente monitoreo.

(Con una excelente supervisión y un producto que se ejecuta como servicio, de hecho es posible aplicar algunas "pruebas" al cliente, pero debe tener un sistema sofisticado que detecte los errores rápidamente y revierta los cambios responsables. Lo más probable es que esto sea demasiado para un pequeño equipo de desarrollo)

El principal beneficio de las pruebas unitarias es que obligan al desarrollador a escribir código mejor diseñado. El acto de probar desafía al desarrollador a separar las preocupaciones y encapsular los datos. También alienta al desarrollador a usar interfaces y otras abstracciones para que pruebe solo una cosa.

Alex Schearer
fuente
De acuerdo, excepto que las pruebas unitarias no obligan a los desarrolladores a escribir un buen código. Hay otra opción: pruebas realmente mal escritas. Debe comprometerse con la idea de las pruebas unitarias de sus codificadores para evitar que se cansen.
Tenpn
2
Claro, pero esa no es razón para tirar al bebé con el agua del baño. Use las herramientas de manera responsable, pero primero use las herramientas.
Alex Schearer