Pruebas unitarias en marcos de visualización (gráficos 3D)

8

Este es un seguimiento de esta pregunta. Allí estaba preguntando cómo hacer pruebas unitarias cuando tienes una biblioteca de algoritmos científicos. Tengo un problema similar ahora pero con un proyecto diferente.

Estoy trabajando en una abstracción del marco del motor de gráficos 3D para DirectX, OpenGl, WebGl, Silverlight, WPF, y básicamente cualquier API 3D que exista, en C #, llamada Rendering .NET . El escenario es el siguiente, el proyecto está siendo desarrollado por un pequeño equipo de colegas míos, todos trabajando en la misma oficina. Todos somos científicos informáticos, no nos interesa mucho la comunidad y las prácticas de ingeniería de software. Incluso tuve dificultades para convencerme de cambiar de Subversion a Git y publicar el proyecto en Codeplex. El proyecto se está haciendo grande (alrededor de 80K líneas de C #), y ahora cubre la mayor parte de DirectX y OpenGl hasta Shader Model 5.0, por lo que no es un proyecto de un solo hombre como el que se describe en la pregunta vinculada. También queremos alentar a la comunidad de código abierto a colaborar.

Hasta ahora hemos estado probando escribiendo pequeñas aplicaciones que implican inicializar todos los dispositivos y recursos, configurar una escena y dibujar. La cuestión es que no sé cómo hacerlo diferente, es decir, cómo diseñar pequeños casos de prueba que prueben características específicas del marco, como la asignación de recursos, la teselación primitiva, la compilación de sombreadores, etc., sin tener que recrear el Inicialización completa del motor. Para algunos de estos casos, puedo pensar en simulacros útiles, pero en general, ¿cómo puedo probar la corrección de un algoritmo de renderizado cuando la salida es visual (una imagen o una escena animada)? En este momento, lo más inteligente que hemos encontrado es renderizar la misma escena a un procesador de software, DirectX y OpenGl, y compararlos píxel por píxel,

Entonces, ¿cuál es el enfoque de prueba "correcto" aquí?

Alejandro Piad
fuente
Podría implementar niveles de tolerancia en su comparación píxel por píxel, pero eso sería un juego de falsos negativos versus falsos positivos, y aún estaría probando en parte la implementación de DirectX / OpenGL, no solo su código (entonces, en realidad no pruebas unitarias , per se). En la mayoría de los escenarios "normales", querrás burlarte de la API de destino y asegurarte de que se llame de acuerdo con tus suposiciones; si su código de representación está llamando directamente a las diversas API, quizás podría introducir su propia capa intermedia (simulable), aunque con una penalización de rendimiento.
Daniel B
También debo agregar que creo que la respuesta de Doc Brown es esencialmente correcta, debería (idealmente) tener partes realmente independientes que pueda probar de forma aislada. Sin embargo, en realidad, si no ha diseñado su solución desde cero para la capacidad de prueba de la unidad, esto podría ser difícil de adaptar.
Daniel B
@DanielB: exactamente lo que pensé al leer que 80K de código ya existe. Este es el código heredado, como lo define Michael Feathers, código sin pruebas. Y el libro canónico para este tipo de situaciones es siempre amazon.de/Working-Effectively-Legacy-Robert-Martin/dp/…
Doc Brown

Respuestas:

8

El enfoque de prueba "correcto" sería desacoplar su lógica de dibujo de las llamadas de DirectX u OpenGL, para que pueda burlarse de esta última (y como otro caso de uso, use la misma lógica de negocios para DirectX u OpenGL).

No desea probar si DirectX u OpenGL funcionan correctamente, ese es el trabajo de Microsoft. También desea que el código de inicialización de su dispositivo se escriba y pruebe solo una vez (y luego se reutilice), por lo que una prueba manual de esa parte debería ser asequible. Si también desea probar esa parte automáticamente, use su enfoque de píxel por píxel, pero con un dibujo de prueba muy simple. Por lo tanto, concéntrese en escribir pruebas de regresión automatizadas para su lógica de dibujo desacoplada, que le brindará el mayor beneficio.

Entonces, la idea es dividir su código en componentes realmente independientes : su inicialización de DirectX no debe estar acoplada a la lógica de dibujo, y la lógica de dibujo no debe depender de DirectX, esto hace que su programa sea comprobable.

EDITAR: encontré esta publicación anterior en pruebas unitarias para el código OpenGL, solo hubo algunas respuestas, pero tal vez traerá algunas ideas adicionales.

Doc Brown
fuente
Parece que este enfoque no cubriría una gran parte de lo que trata esta biblioteca.
Kris Van Bael
@KrisVanBael: tal vez, tal vez no, solo estás adivinando. Pero siéntase libre de hacer una mejor sugerencia para un enfoque de prueba en caso de que tenga razón.
Doc Brown
Gracias @DocBrown. El marco está desacoplado de implementaciones de API específicas, a través de muchas interfaces, y estamos utilizando inyección de dependencia en todas partes para resolver administradores de recursos, renderizadores, compiladores y demás, de modo que pueda, por ejemplo, crear una malla con DirectX y visualizarla en OpenGL. ¿Entonces dice que debería escribir una especie de "implementación de software" que se burla de todo el marco y la prueba unitaria allí? Parece agradable. Estoy de acuerdo en que no debería probar DirectX ni OpenGL, solo mi capa intermedia.
Alejandro Piad
@AlejandroPiad: ¿cómo comer un elefante? Un bocado a la vez. Agregue pruebas a lo largo de su hoja de ruta de desarrollo, probando las partes de su programa que va a cambiar a continuación. Su programa no se compiló en una semana, por lo que su marco de prueba no se compilará en ese momento.
Doc Brown
1
@DocBrown muchas gracias. Creo que lo entiendo. Primero comenzaré a burlarme de las partes vitales, y trataré de que todo lo nuevo que agreguemos esté diseñado teniendo en cuenta las pruebas.
Alejandro Piad