Unit-Tests für Visualisierungs-Frameworks (3D-Grafiken)

8

Dies ist eine Fortsetzung dieser Frage. Dort habe ich gefragt, wie man Unit-Tests durchführt, wenn man eine Bibliothek wissenschaftlicher Algorithmen hat. Ich habe jetzt ein ähnliches Problem, aber mit einem anderen Projekt.

Ich arbeite an einer 3D-Grafik-Engine-Framework-Abstraktion für DirectX, OpenGl, WebGl, Silverlight, WPF und im Grunde genommen an der 3D-API unter C # namens Rendering .NET . Das Szenario ist das folgende: Das Projekt wird von einem kleinen Team von Kollegen entwickelt, die alle im selben Büro arbeiten. Wir sind alle Informatiker, nicht viel in der Software-Engineering-Community und -Praktiken. Es fiel mir sogar schwer, zu überzeugen, von Subversion zu Git zu wechseln und das Projekt auf Codeplex zu veröffentlichen. Das Projekt wird immer umfangreicher (ca. 80.000 Zeilen C #) und deckt jetzt den größten Teil von DirectX und OpenGl bis Shader Model 5.0 ab. Es handelt sich also nicht um ein Ein-Mann-Projekt wie das in der verknüpften Frage beschriebene. Wir möchten auch die Open Source-Community zur Zusammenarbeit ermutigen.

Bisher haben wir getestet, indem wir kleine Anwendungen geschrieben haben, bei denen alle Geräte und Ressourcen initialisiert, eine Szene eingerichtet und gezeichnet wurden. Die Sache ist, ich weiß nicht, wie ich es anders machen soll, ich meine, wie man kleine Testfälle entwirft, die bestimmte Merkmale des Frameworks wie Ressourcenzuweisung, primitive Tessellation, Shader-Kompilierung usw. testen, ohne das neu erstellen zu müssen gesamte Motorinitialisierung. In einigen dieser Fälle kann ich mir nützliche Verspottungen vorstellen, aber wie kann ich im Allgemeinen die Richtigkeit eines Rendering-Algorithmus testen, wenn die Ausgabe visuell ist (ein Bild oder eine animierte Szene)? Im Moment ist es am klügsten, dieselbe Szene mit einem Software-Renderer, DirectX und OpenGl, zu rendern und sie Pixel für Pixel zu vergleichen.

Was ist hier der "richtige" Testansatz?

Alejandro Piad
quelle
Sie könnten Toleranzstufen in Ihren Pixel-für-Pixel-Vergleich implementieren, aber das wäre ein Spiel zwischen falsch negativen und falsch positiven Ergebnissen, und Sie würden die DirectX / OpenGL-Implementierung immer noch teilweise testen, nicht nur Ihren Code (also) nicht wirklich Unit-Test an sich). In den meisten "normalen" Szenarien möchten Sie die Ziel-API verspotten und sicherstellen, dass sie gemäß Ihren Annahmen aufgerufen wird. Wenn Ihr Rendering-Code die verschiedenen APIs direkt aufruft, können Sie möglicherweise Ihre eigene Zwischenschicht (verspottbar) einführen, wenn auch mit einem Leistungsverlust.
Daniel B
Ich sollte auch hinzufügen, dass ich glaube, dass Doc Browns Antwort im Wesentlichen richtig ist. Sie sollten (idealerweise) wirklich unabhängige Teile haben, die Sie isoliert testen können. In der Realität kann es jedoch schwierig sein, diese Lösung nachzurüsten, wenn Sie Ihre Lösung nicht von Grund auf auf Testbarkeit der Einheiten ausgelegt haben.
Daniel B
@ DanielB: genau das, was ich beim Lesen gedacht habe, dass 80K Code bereits vorhanden sind. Dies ist Legacy-Code - wie Michael Feathers ihn definiert - Code ohne Tests. Und das kanonische Buch für diese Art von Situation ist immer amazon.de/Working-Effectively-Legacy-Robert-Martin/dp/…
Doc Brown

Antworten:

8

Der "richtige" Testansatz besteht darin, Ihre Zeichnungslogik von den DirectX- oder OpenGL-Aufrufen zu entkoppeln, damit Sie letztere verspotten können (und als weiteren Anwendungsfall dieselbe Geschäftslogik entweder für DirectX oder OpenGL verwenden können).

Sie möchten nicht testen, ob DirectX oder OpenGL ordnungsgemäß funktionieren - das ist der Job von Microsoft. Sie möchten auch, dass Ihr Geräteinitialisierungscode nur einmal geschrieben und getestet (und dann wiederverwendet) wird. Daher sollte ein manueller Test dieses Teils erschwinglich sein. Wenn Sie diesen Teil auch automatisch testen möchten, verwenden Sie Ihren pixelweisen Ansatz, jedoch mit einer sehr einfachen Testzeichnung. Konzentrieren Sie sich daher darauf, automatisierte Regressionstests für Ihre entkoppelte Zeichenlogik zu schreiben, die Ihnen den größten Nutzen bringen.

Die ganze Idee besteht also darin, Ihren Code in wirklich unabhängige Komponenten aufzuteilen - Ihre DirectX-Initialisierung sollte nicht an die Zeichnungslogik gekoppelt sein und die Zeichnungslogik sollte nicht von DirectX abhängen - dies macht Ihr Programm testbar.

BEARBEITEN: fand diesen früheren Beitrag über Unit-Tests für OpenGL-Code, es gab nur wenige Antworten, aber vielleicht bringt es einige zusätzliche Erkenntnisse.

Doc Brown
quelle
Es scheint, dass dieser Ansatz keinen großen Teil dessen abdecken würde, worum es in dieser Bibliothek geht.
Kris Van Bael
@KrisVanBael: Vielleicht, vielleicht auch nicht, raten Sie nur. Sie können jedoch gerne einen besseren Vorschlag für einen Testansatz machen, falls Sie Recht haben.
Doc Brown
Danke @DocBrown. Das Framework ist über viele Schnittstellen von bestimmten API-Implementierungen entkoppelt, und wir verwenden die Abhängigkeitsinjektion überall, um Ressourcenmanager, Renderer, Compiler usw. zu lösen, sodass Sie beispielsweise ein Netz mit DirectX erstellen und es visualisieren können OpenGL. Sie sagen also, ich sollte eine Art "Software-Implementierung" schreiben, die das gesamte Framework und den Unit-Test dort verspottet? Scheint gut zu sein. Ich bin damit einverstanden, dass ich weder DirectX noch OpenGL testen sollte, sondern nur meine mittlere Schicht.
Alejandro Piad
@AlejandroPiad: Wie man einen Elefanten isst? Ein Bissen nach dem anderen. Fügen Sie Tests entlang Ihrer Entwicklungs-Roadmap hinzu und testen Sie die Teile Ihres Programms, die Sie als Nächstes ändern werden. Ihr Programm wurde nicht in einer Woche erstellt, sodass Ihr Testframework in dieser Zeit nicht erstellt wird.
Doc Brown
1
@ DocBrown vielen Dank. Ich glaube ich verstehe. Ich werde mich zuerst über die wichtigen Teile lustig machen und versuchen, dass alles, was wir neu hinzufügen, auf Tests ausgelegt ist.
Alejandro Piad