Lohnt es sich, Komponententests für wissenschaftliche Forschungscodes zu schreiben?

89

Ich bin fest davon überzeugt, dass die Verwendung von Tests, die ein vollständiges Programm verifizieren (z. B. Konvergenztests), einschließlich eines automatisierten Satzes von Regressionstests , von Nutzen ist . Nachdem ich einige Programmierbücher gelesen habe, habe ich das quälende Gefühl, dass ich Unit-Tests schreiben sollte (dh Tests, die die Richtigkeit einer einzelnen Funktion überprüfen und nicht den gesamten Code ausführen, um ein Problem zu lösen) . Allerdings Unit - Tests scheinen nicht immer passen mit wissenschaftlichen Codes und künstlichen oder wie eine Verschwendung von Zeit am Ende des Gefühl.

Sollten wir Komponententests für Forschungscodes schreiben?

David Ketcheson
quelle
2
Dies ist eine offene Frage, nicht wahr?
Qubyte
2
Wie bei allen "Regeln" gilt immer eine Dosis kritisches Denken. Fragen Sie sich, ob eine bestimmte Routine eine offensichtliche Möglichkeit zum Testen von Einheiten bietet. Wenn nicht, ist entweder ein Komponententest zu diesem Zeitpunkt nicht sinnvoll, oder der Code war schlecht gestaltet. Im Idealfall führt eine Routine eine Aufgabe so unabhängig wie möglich von anderen Routinen aus, dies muss jedoch gelegentlich abgewickelt werden.
Lagerbaer
In ähnlicher Weise gibt es eine gute Diskussion über eine Frage zum Stackoverflow .
Naught101

Antworten:

85

Viele Jahre lang hatte ich das Missverständnis, dass ich nicht genug Zeit hatte, Komponententests für meinen Code zu schreiben. Wenn ich Tests schrieb, waren sie aufgebläht, schwere Dinge, die mich nur ermutigten zu glauben, dass ich Komponententests nur schreiben sollte, wenn ich wusste, dass sie gebraucht wurden.

Dann habe ich angefangen, Test Driven Development zu verwenden und fand, dass es eine vollständige Offenbarung ist. Ich bin jetzt fest davon überzeugt, dass ich nicht die Zeit habe, Unit-Tests nicht zu schreiben .

Wenn Sie mit Blick auf das Testen entwickeln, erhalten Sie meiner Erfahrung nach sauberere Schnittstellen, fokussiertere Klassen und Module und im Allgemeinen mehr SOLIDEN , testbaren Code.

Jedes Mal, wenn ich mit Legacy-Code arbeite, der keine Komponententests hat und manuell etwas testen muss, denke ich, "das wäre viel schneller, wenn dieser Code bereits Komponententests hätte". Jedes Mal, wenn ich versuchen muss, dem Code mit hoher Kopplung Unit-Test-Funktionen hinzuzufügen, denke ich, dass dies so viel einfacher wäre, wenn er entkoppelt geschrieben worden wäre.

Vergleich und Gegenüberstellung der beiden von mir unterstützten Versuchsstationen. Einer ist schon eine Weile da und hat viel älteren Code, während der andere relativ neu ist.

Wenn Sie dem alten Labor Funktionen hinzufügen, müssen Sie oft mehrere Stunden damit verbringen, sich mit den Auswirkungen der von Ihnen benötigten Funktionen und der Art und Weise, wie ich diese Funktionen hinzufügen kann, ohne die anderen Funktionen zu beeinträchtigen, auseinanderzusetzen. Der Code ist einfach nicht für Offline-Tests eingerichtet, daher muss so ziemlich alles online entwickelt werden. Wenn ich versuchen würde, offline zu entwickeln, würde ich am Ende mehr Scheinobjekte haben, als vernünftig wären.

Im neueren Labor kann ich normalerweise Funktionen hinzufügen, indem ich sie offline an meinem Schreibtisch entwickle, nur die Dinge verspotte, die sofort benötigt werden, und dann nur eine kurze Zeit im Labor verbringe, um alle verbleibenden Probleme auszubügeln, die nicht behoben wurden -Linie.

Aus Gründen der Übersichtlichkeit und da @ naught101 gefragt ...

Ich arbeite in der Regel an Software für experimentelle Steuerung und Datenerfassung mit Ad-hoc-Datenanalyse. Daher hilft die Kombination von TDD mit Revisionskontrolle, sowohl Änderungen der zugrunde liegenden Experiment-Hardware als auch Änderungen der Datenerfassungsanforderungen im Laufe der Zeit zu dokumentieren.

Sogar in der Situation, in der der Explorationscode entwickelt wurde, konnte ich einen signifikanten Vorteil aus der Kodifizierung von Annahmen und der Fähigkeit erkennen, die Entwicklung dieser Annahmen im Laufe der Zeit zu beobachten.

Mark Booth
quelle
7
Mark, um welche Art von Code handelt es sich hier? Wiederverwendbares Modell? Ich stelle fest, dass diese Überlegung nicht wirklich auf Dinge wie explorativen Datenanalysecode zutrifft, bei denen man wirklich viel herumspringen muss und oft nicht erwartet, den Code irgendwo anders wiederzuverwenden.
Naught101
35

Wissenschaftliche Codes haben in der Regel häufiger Konstellationen ineinandergreifender Funktionen als die Geschäftscodes, an denen ich gearbeitet habe, normalerweise aufgrund der mathematischen Struktur des Problems. Daher halte ich Unit-Tests für einzelne Funktionen nicht für sehr effektiv. Ich denke jedoch, dass es eine Klasse von Komponententests gibt, die effektiv sind und sich von ganzen Programmtests insofern stark unterscheiden, als sie auf bestimmte Funktionen abzielen.

Ich definiere nur kurz, was ich unter solchen Tests verstehe. Beim Regressionstest wird nach Änderungen am vorhandenen Verhalten gesucht (die auf irgendeine Weise validiert werden), wenn Änderungen am Code vorgenommen werden. Beim Unit-Test wird ein Teil des Codes ausgeführt und anhand einer Spezifikation überprüft, ob die gewünschte Ausgabe erfolgt. Sie sind nicht so unterschiedlich, da der ursprüngliche Regressionstest ein Komponententest war , da ich feststellen musste, dass die Ausgabe gültig war.

hChrrr

Zwei weitere Beispiele für Unit-Tests, die von PyLith stammen , sind die Punktlokalisierung , bei der es sich um eine einzelne Funktion handelt, für die sich leicht synthetische Ergebnisse erzielen lassen, und die Erzeugung von kohäsiven Zellen mit null Volumen in einem Netz, das mehrere Funktionen umfasst, jedoch ein umschriebenes Teil von adressiert Funktionalität im Code.

Es gibt viele Tests dieser Art, einschließlich Konservierungs- und Konsistenztests. Die Operation unterscheidet sich nicht wesentlich von der Regression (Sie führen einen Test durch und vergleichen die Ausgabe mit einem Standard), die Standardausgabe stammt jedoch aus einer Spezifikation und nicht aus einem vorherigen Durchlauf.

Matt Knepley
quelle
4
Wikipedia sagt: "Unit-Tests, auch Komponententests genannt, beziehen sich auf Tests, die die Funktionalität eines bestimmten Codeabschnitts überprüfen, normalerweise auf Funktionsebene." Konvergenztests in einem Finite-Elemente-Code können eindeutig keine Komponententests sein, da sie viele Funktionen beinhalten.
David Ketcheson
Deshalb habe ich am Anfang des Beitrags klargestellt, dass ich den weiten Blick auf Unit-Tests habe und "normalerweise" genau das bedeutet.
Matt Knepley
Meine Frage war im Sinne der allgemein akzeptierten Definition von Komponententests gemeint. Ich habe dies jetzt in der Frage ganz deutlich gemacht.
David Ketcheson
Ich habe meine Antwort geklärt
Matt Knepley
Ihre letzteren Beispiele sind relevant für das, was ich beabsichtigt habe.
David Ketcheson
28

Seit ich in Code Complete, 2. Ausgabe , über testgesteuerte Entwicklung gelesen habe, verwende ich ein Unit-Testing-FrameworkDies ist Teil meiner Entwicklungsstrategie und hat meine Produktivität erheblich gesteigert, da ich weniger Zeit für das Debuggen aufgewendet habe, da die verschiedenen Tests, die ich schreibe, diagnostisch sind. Als Nebeneffekt bin ich viel zuversichtlicher in meine wissenschaftlichen Ergebnisse und habe meine Unit-Tests mehrfach genutzt, um meine Ergebnisse zu verteidigen. Wenn bei einem Komponententest ein Fehler auftritt, kann ich normalerweise schnell herausfinden, warum. Wenn meine Anwendung abstürzt und alle Unit-Tests bestanden wurden, analysiere ich die Codeabdeckung, um festzustellen, welche Teile meines Codes nicht ausgeführt wurden, und gehe den Code mit einem Debugger durch, um die Fehlerquelle zu lokalisieren. Dann schreibe ich einen neuen Test, um sicherzustellen, dass der Fehler behoben bleibt.

Viele der Tests, die ich schreibe, sind keine reinen Unit-Tests. Streng definiert, sollen Unit-Tests die Funktionalität einer Funktion ausüben. Wenn ich einfach eine einzelne Funktion mit Scheindaten testen kann, mache ich das. Manchmal kann ich die Daten, die ich zum Schreiben eines Tests benötige, der die Funktionalität einer bestimmten Funktion ausübt, nicht einfach verspotten. Daher teste ich diese Funktion zusammen mit anderen in einem Integrationstest. IntegrationstestsTesten Sie das Verhalten mehrerer Funktionen gleichzeitig. Wie Matt betont, sind wissenschaftliche Codes oft eine Konstellation von ineinandergreifenden Funktionen, aber oft werden bestimmte Funktionen nacheinander aufgerufen, und es können Komponententests geschrieben werden, um die Ausgabe in Zwischenschritten zu testen. Wenn mein Produktionscode beispielsweise fünf Funktionen nacheinander aufruft, schreibe ich fünf Tests. Der erste Test ruft nur die erste Funktion auf (es handelt sich also um einen Komponententest). Dann ruft der zweite Test die erste und die zweite Funktion auf, der dritte Test ruft die ersten drei Funktionen auf und so weiter. Selbst wenn ich Unit-Tests für jede einzelne Funktion in meinem Code schreiben könnte, würde ich trotzdem Integrationstests schreiben, da Fehler auftreten können, wenn verschiedene modulare Teile eines Programms kombiniert werden. Nachdem ich alle Unit-Tests und Integrationstests geschrieben habe, von denen ich denke, dass ich sie brauche, habe ich Ich verpacke meine Fallstudien in Unit-Tests und verwende sie für Regressionstests, da ich möchte, dass meine Ergebnisse wiederholbar sind. Wenn sie nicht wiederholbar sind und ich unterschiedliche Ergebnisse erhalte, möchte ich wissen, warum. Das Scheitern eines Regressionstests ist vielleicht kein wirkliches Problem, aber es zwingt mich herauszufinden, ob die neuen Ergebnisse mindestens so zuverlässig sind wie die alten.

Neben Unit-Tests lohnen sich auch statische Codeanalysen, Speicher-Debugger und das Kompilieren mit Compiler-Warnflags, um einfache Fehler und nicht verwendeten Code abzufangen.

Geoff Oxberry
quelle
verwandte
frage
Würden Sie Integrationstests als ausreichend erachten oder denken Sie, dass Sie auch separate Komponententests schreiben müssen?
Siamii
Ich würde separate Unit-Tests schreiben, wo immer dies möglich und machbar ist. Es erleichtert das Debuggen und erzwingt entkoppelten Code (was Sie wollen).
Geoff Oxberry
19

Meiner Erfahrung nach muss die Programmierung mit zunehmender Komplexität der Codes für wissenschaftliche Forschung sehr modular sein. Dies kann für Codes mit einer großen und alten Basis ( f77irgendjemand?) Schmerzhaft sein, aber es ist notwendig, vorwärts zu gehen. Da ein Modul um einen bestimmten Aspekt des Codes herum aufgebaut wird (für CFD-Anwendungen, denken Sie an Randbedingungen oder Thermodynamik), sind Komponententests sehr wertvoll, um die neue Implementierung zu validieren und Probleme und weitere Softwareentwicklungen zu isolieren.

Diese Einheitentests sollten eine Stufe unter der Codeüberprüfung liegen (kann ich die analytische Lösung meiner Wellengleichung wiederherstellen?) Und zwei Stufen unter der Codeüberprüfung liegen (kann ich die korrekten Spitzen-Effektivwerte in meinem turbulenten Rohrfluss vorhersagen), um einfach sicherzustellen, dass die Programmierung korrekt ist (Sind die Argumente richtig übergeben worden, zeigen die Zeiger auf das Richtige?) und "math" (diese Unterroutine berechnet den Reibungskoeffizienten. Wenn ich eine Menge von Zahlen eingebe und die Lösung von Hand berechne, ergibt die Routine dasselbe.) Ergebnis?) sind korrekt. Grundsätzlich eine Ebene über das hinausgehen, was die Compiler erkennen können, dh grundlegende Syntaxfehler.

Ich würde es auf jeden Fall für einige wichtige Module in Ihrer Anwendung empfehlen. Man muss sich jedoch darüber im Klaren sein, dass dies äußerst mühsam und zeitaufwendig ist. Wenn Sie nicht über unbegrenzte personelle Ressourcen verfügen, würde ich es nicht für 100% eines komplexen Codes empfehlen.

FranzösischKheldar
quelle
Haben Sie spezielle Beispiele oder Kriterien, anhand derer Sie auswählen können, welche Teile einer Einzelprüfung unterzogen werden sollen (und welche nicht)?
David Ketcheson
@DavidKetcheson Meine Erfahrung ist begrenzt durch die von uns verwendete Anwendung und Sprache. Für unseren Allzweck-CFD-Code mit etwa 200.000 Zeilen, hauptsächlich F90, haben wir in den letzten ein oder zwei Jahren versucht, einige Funktionen des Codes wirklich zu isolieren. Das Erstellen eines Moduls und dessen Verwendung über den gesamten Code hinweg bringt dies nicht, daher muss man diese Module wirklich vergleichen und praktisch zu Bibliotheken machen. Daher werden nur sehr wenige USE-Anweisungen und alle Verbindungen mit dem Rest des Codes über Routineaufrufe ausgeführt. Routinen, die Sie natürlich nicht testen können, sowie der Rest der Bibliothek.
FrenchKheldar
@DavidKetcheson Wie ich in meiner Antwort sagte, waren die Randbedingungen und die Thermodynamik zwei Aspekte unseres Codes, die wir wirklich isolieren konnten und die so wenig sinnvoll waren. Ganz allgemein würde ich mit etwas Kleinem beginnen und versuchen, es sauber zu machen. Idealerweise ist dies ein 2-Personen-Job. Eine Person schreibt die Routinen und die Dokumentation, die die Schnittstelle beschreiben, eine andere Person sollte den Komponententest schreiben, im Idealfall ohne den Quellcode zu betrachten und nur anhand der Schnittstellenbeschreibung. Auf diese Weise wird die Absicht der Routine getestet, aber ich erkenne, dass dies keine einfache Sache ist, die man organisieren kann.
FrenchKheldar
1
Warum nicht die anderen Arten von Software-Tests (Integration, System) zusätzlich zu Unit-Tests einbeziehen? Wäre dies neben Zeit und Kosten nicht die umfassendste technische Lösung? Meine Referenzen sind 1 (Abschnitt 3.4.2) und 2 (Seite 5). Mit anderen Worten, sollte der Quellcode nicht mit den herkömmlichen Softwareteststufen 3 ("Teststufen") getestet werden ?
Ximiki
14

Unit-Tests für wissenschaftliche Codes sind aus verschiedenen Gründen nützlich.

Drei sind insbesondere:

  • Unit-Tests helfen anderen, die Einschränkungen Ihres Codes zu verstehen. Unit-Tests sind im Grunde genommen eine Form der Dokumentation.

  • Unit-Tests stellen sicher, dass eine einzelne Codeeinheit die richtigen Ergebnisse liefert, und stellen sicher, dass sich das Verhalten eines Programms nicht ändert, wenn die Details geändert werden.

  • Mit Unit-Tests können Sie Ihre Research-Codes einfacher modularisieren. Dies kann besonders wichtig sein, wenn Sie versuchen, Ihren Code auf eine neue Plattform auszurichten, z. B. wenn Sie ihn parallelisieren oder auf einem GPGPU-Computer ausführen möchten.

Unit-Tests geben Ihnen vor allem die Gewissheit, dass die Forschungsergebnisse, die Sie mit Ihren Codes erstellen, gültig und überprüfbar sind.

Ich stelle fest, dass Sie in Ihrer Frage Regressionstests erwähnen. In vielen Fällen werden Regressionstests durch die automatisierte, regelmäßige Ausführung von Komponententests und / oder Integrationstests (die prüfen, ob Codeteile in Kombination korrekt funktionieren) durchgeführt. Beim wissenschaftlichen Rechnen erfolgt dies häufig durch Vergleich der Ausgabe mit experimentellen Daten oder dem Ergebnisse früherer Programme, denen vertraut wird). Es hört sich so an, als würden Sie bereits Integrationstests oder Unit-Tests auf der Ebene großer komplexer Komponenten erfolgreich durchführen.

Ich würde sagen, dass es angesichts der zunehmenden Komplexität von Forschungscodes und der Abhängigkeit von den Codes und Bibliotheken anderer Personen wichtig ist, zu verstehen, wo der Fehler auftritt, wenn er auftritt. Durch Unit-Tests kann der Fehler viel einfacher lokalisiert werden.

Die Beschreibung, Nachweise und Verweise finden Sie in Abschnitt 7 "Plan for mistakes" (Plan für Fehler) des Artikels, den ich als Co-Autor für Best Practices for Scienti fi c Computing verfasst habe. Außerdem wird das ergänzende Konzept der defensiven Programmierung vorgestellt.

Neil Chue Hong
quelle
9

In meinen deal.II-Klassen unterrichte ich, dass Software, die keine Tests hat, nicht richtig funktioniert (und betone, dass ich absichtlich gesagt habe: " funktioniert nicht richtig", nicht " funktioniert möglicherweise nicht richtig).

Natürlich lebe ich nach dem Mantra - so läuft das Geschäft. Ich habe 2.500 Tests mit jedem Commit durchgeführt ;-)

Im Ernst, ich denke, Matt definiert die beiden Testklassen bereits gut. Wir schreiben Komponententests für das Zeug auf niedrigerer Ebene und es entwickelt sich auf natürliche Weise zu Regressionstests für das Zeug auf höherer Ebene. Ich glaube nicht, dass ich eine klare Grenze ziehen könnte, die unsere Tests von der einen oder anderen Seite trennen würde. Es gibt sicherlich viele, die die Grenze überschreiten, in der sich jemand die Ausgabe angeschaut hat und diese als weitgehend vernünftig ansieht (Komponententest?). ohne es bis auf die letzte Genauigkeit angeschaut zu haben (Regressionstest?).

Wolfgang Bangerth
quelle
Warum schlagen Sie diese Hierarchie (Einheit für Niedrigere, Regression für Höhere) im Vergleich zu den traditionellen Ebenen des Softwaretests vor?
Ximiki
@ximiki - das meine ich nicht. Ich sage, dass es Tests für ein Spektrum gibt, das alle in Ihrem Link aufgeführten Kategorien umfasst.
Wolfgang Bangerth
8

Ja und nein. Sicherlich nicht geeignet für grundlegende Routinen des Basis-Toolsets, mit dem Sie Ihr Leben vereinfachen, z. B. Konvertierungsroutinen, Zeichenfolgenzuordnungen, grundlegende Physik und Mathematik usw. Wenn es um Berechnungsklassen oder -funktionen geht, können sie im Allgemeinen lange Laufzeiten erfordern Sie können es sogar vorziehen, sie als Funktionstests anstatt als Einheiten zu testen. Unittest und stress viel auch jene Klassen und Entitäten, deren Level und Verwendung sich stark verändern werden (zB zu Optimierungszwecken) oder deren interne Details aus irgendeinem Grund geändert werden. Das typischste Beispiel ist eine Klasse, die eine riesige Matrix umschließt, die von der Festplatte abgebildet wird.

Stefano Borini
quelle
7

Absolut!

Was, das reicht dir nicht?

In der wissenschaftlichen Programmierung entwickeln wir uns mehr als in jeder anderen Art auf der Grundlage des Versuchs, ein physikalisches System zu finden. Woher wissen Sie, ob Sie das anders gemacht haben als durch Testen? Überlegen Sie sich, wie Sie Ihren Code verwenden möchten, bevor Sie mit dem Codieren beginnen, und führen Sie einige Beispielläufe aus. Versuchen Sie, mögliche Randfälle aufzufangen. Tun Sie dies auf modulare Weise - zum Beispiel können Sie für ein neuronales Netzwerk eine Reihe von Tests für ein einzelnes Neuron und eine Reihe von Tests für ein vollständiges neuronales Netzwerk durchführen. Auf diese Weise können Sie beim Schreiben von Code sicherstellen, dass Ihr Neuron funktioniert, bevor Sie mit der Arbeit im Netzwerk beginnen. In solchen Phasen zu arbeiten bedeutet, dass Sie, wenn Sie auf ein Problem stoßen, nur die letzte 'Phase' des zu testenden Codes haben, die früheren Phasen bereits getestet wurden.

Sobald Sie die Tests abgeschlossen haben, müssen Sie den Code in einer anderen Sprache umschreiben (z. B. in CUDA konvertieren) oder sogar, wenn Sie ihn nur aktualisieren, haben Sie bereits die Testfälle und können sie verwenden, um sie zu erstellen Stellen Sie sicher, dass beide Versionen Ihres Programms gleich funktionieren.

Dan
quelle
+1: "Wenn Sie in solchen Phasen arbeiten, bedeutet dies, dass Sie nur die letzte 'Phase' des Codes testen müssen, wenn Sie auf ein Problem stoßen. Die früheren Phasen wurden bereits getestet."
Ximiki
5

Ja.

Die Idee, dass jeder Code ohne Unit-Tests geschrieben wird, ist ein Gräuel. Es sei denn, Sie beweisen, dass Ihr Code korrekt ist, und beweisen dann, dass der Beweis korrekt ist = P.

aterrel
quelle
3
... und dann beweisen Sie, dass der Beweis, dass der Beweis richtig ist, und ... nun, das ist ein tiefes Kaninchenloch.
JM
2
Schildkröten machen Dijkstra stolz!
Aterrel
2
Lösen Sie einfach den allgemeinen Fall und lassen Sie Ihren Beweis sich als richtig erweisen! Torus der Schildkröten!
Aesin
5

Ich würde diese Frage eher pragmatisch als dogmatisch angehen. Stellen Sie sich die Frage: "Was könnte in Funktion X schief gehen?" Stellen Sie sich vor, was mit der Ausgabe passiert, wenn Sie einige typische Fehler in den Code einfügen: einen falschen Präfaktor, einen falschen Index, ... und schreiben Sie dann Komponententests, die diese Art von Fehler wahrscheinlich erkennen. Wenn es für eine gegebene Funktion keine Möglichkeit gibt, solche Tests zu schreiben, ohne den Code der Funktion selbst zu wiederholen, dann tun Sie dies nicht - sondern denken Sie an Tests auf der nächsthöheren Ebene.

Ein viel wichtigeres Problem bei Komponententests (oder in der Tat bei allen Tests) im wissenschaftlichen Code ist der Umgang mit den Unsicherheiten der Gleitkomma-Arithmetik. Soweit ich weiß, gibt es noch keine guten allgemeinen Lösungen.

khinsen
quelle
Unabhängig davon, ob Sie manuell oder automatisch mit Unit-Tests testen, treten bei der Gleitkommadarstellung genau dieselben Probleme auf. Ich würde Richard Harris' sehr empfehlen ausgezeichnete Serie von Artikeln in AKKU ‚s Überlastung Magazin .
Mark Booth
Msgstr "Wenn es für eine gegebene Funktion keine Möglichkeit gibt, solche Tests zu schreiben, ohne den Code der Funktion selbst zu wiederholen, dann nicht". Können Sie näher darauf eingehen? Ein Beispiel würde mir das verdeutlichen.
Ximiki
5

Tangurena tut mir leid - hier ist das Mantra "Ungetesteter Code ist gebrochener Code" und das kam vom Chef. Anstatt all die guten Gründe für Unit-Tests zu wiederholen, möchte ich nur ein paar Details hinzufügen.

  • Die Speichernutzung sollte getestet werden. Jede Funktion, die Speicher zuweist, sollte getestet werden, um sicherzustellen, dass die Funktionen, die Daten in diesem Speicher speichern und abrufen, das Richtige tun. Dies ist in der GPU-Welt noch wichtiger.
  • Das Testen von Edge Cases ist, wie bereits erwähnt, äußerst wichtig. Denken Sie an diese Tests genauso, wie Sie das Ergebnis einer Berechnung testen. Stellen Sie sicher, dass sich der Code an den Kanten verhält und ordnungsgemäß fehlschlägt (wie auch immer Sie dies in Ihrer Simulation definieren), wenn Eingabeparameter oder Daten außerhalb akzeptabler Grenzen liegen. Das Denken, das mit dem Schreiben dieser Art von Tests verbunden ist, hilft Ihnen dabei, Ihre Arbeit zu schärfen, und kann einer der Gründe sein, warum Sie selten jemanden finden, der Komponententests geschrieben hat, den Prozess jedoch nicht nützlich findet.
  • Verwenden Sie ein Testframework (wie von Geoff erwähnt, der einen netten Link bereitgestellt hat). Ich habe das BOOST-Testframework in Verbindung mit dem CTest-System von CMake verwendet und kann es als einfache Methode zum schnellen Schreiben von Komponententests (sowie Validierungs- und Regressionstests) empfehlen.
user69
quelle
+1: "Stellen Sie sicher, dass sich der Code an den Kanten verhält und ordnungsgemäß fehlschlägt (wie auch immer Sie dies in Ihrer Simulation definieren), wenn Eingabeparameter oder Daten außerhalb akzeptabler Grenzen liegen."
Ximiki
5

Ich habe Unit-Tests für mehrere kleine Codes (dh für einzelne Programmierer) erfolgreich eingesetzt, einschließlich der dritten Version meines Dissertations-Analysecodes in der Teilchenphysik.

Die ersten beiden Versionen waren unter ihrem eigenen Gewicht und der Vervielfachung von Zusammenhängen zusammengebrochen.

Andere haben geschrieben, dass die Interaktion zwischen Modulen oft der Ort ist, an dem die wissenschaftliche Codierung bricht, und sie haben damit Recht. Es ist jedoch viel einfacher, diese Probleme zu diagnostizieren , wenn Sie schlüssig nachweisen können, dass jedes Modul das tut, was es soll.

dmckee
quelle
3

Ein etwas anderer Ansatz, den ich bei der Entwicklung eines chemischen Lösers (für komplexe geologische Domänen) verwendet habe, war das, was man Unit Testing per Copy and Paste Snippet nennen kann .

Der Bau eines Testkabels für den ursprünglichen Code, der in einen großen Modellierer für chemische Systeme eingebettet war, war im Zeitrahmen nicht realisierbar.

Es gelang mir jedoch, eine immer komplexer werdende Sammlung von Ausschnitten zu erstellen, die die Funktionsweise des (Boost Spirit) -Parsers für die chemischen Formeln als Einheitentests für verschiedene Ausdrücke zeigten.

Der letzte, komplexeste Komponententest kam dem im System benötigten Code sehr nahe, ohne dass dieser Code geändert werden musste, um nachahmbar zu sein. Auf diese Weise konnte ich meinen einheitlich getesteten Code kopieren.

Was dies zu mehr als nur einer Lernübung und einer echten Regressionssuite macht, sind zwei Faktoren: Die Komponententests werden in der Hauptquelle gespeichert und als Teil anderer Tests für diese Anwendung ausgeführt (und ja, sie haben einen Nebeneffekt von Boost aufgefangen Änderung der Stimmung 2 Jahre später) - Da der in der realen Anwendung kopierte und eingefügte Code nur geringfügig geändert wurde, kann er Kommentare zu den Komponententests enthalten, um die Synchronität zu gewährleisten.

Andy Dent
quelle
2

Für größere Codebasen sind Tests (nicht unbedingt Komponententests) für das High-Level-Zeug nützlich. Unit-Tests für einige einfachere Algorithmen sind ebenfalls nützlich, um sicherzustellen, dass Ihr Code keinen Unsinn macht, da Ihre Hilfsfunktion sinanstelle von verwendet wird cos.

Für den gesamten Forschungscode ist es jedoch sehr schwierig, Tests zu schreiben und zu warten. Algorithmen sind in der Regel sehr umfangreich, ohne dass aussagekräftige Zwischenergebnisse vorliegen, die offensichtliche Tests erfordern und deren Ausführung oft lange dauert, bis ein Ergebnis vorliegt. Natürlich können Sie gegen Referenzläufe testen, die gute Ergebnisse lieferten, aber dies ist kein guter Test im Sinne eines Einheitentests.

Ergebnisse sind oft Annäherungen an die wahre Lösung. Während Sie Ihre einfachen Funktionen testen können, ob sie bis zu einem gewissen Epsilon genau sind, ist es sehr schwierig zu überprüfen, ob z. B. ein Ergebnisgitter korrekt ist oder nicht, was durch visuelle Inspektion durch den Benutzer (Sie) zuvor bewertet wurde.

In solchen Fällen haben automatisierte Tests oft ein zu hohes Kosten-Nutzen-Verhältnis. Ich empfehle etwas besseres: Testprogramme schreiben. Zum Beispiel habe ich ein mittelgroßes Python-Skript geschrieben, um Daten über Ergebnisse wie Histogramme der Kantengrößen und Winkel eines Netzes, Fläche des größten und kleinsten Dreiecks und deren Verhältnis usw. zu erstellen.

Ich kann es verwenden beide Eingangs- und Ausgangs Maschen während des normalen Betriebs zu beurteilen und nutzen es eine Plausibilitätsprüfung zu haben , nachdem ich den Algorithmus verändert. Wenn ich den Algorithmus ändere, weiß ich nicht immer, ob das neue Ergebnis besser ist, weil es oft kein absolutes Maß gibt, welche Approximation die beste ist. Aber wenn ich solche Metriken erstelle, kann ich einige Faktoren beschreiben, die besser sind wie "Die neue Variante hat irgendwann ein besseres Winkelverhältnis, aber eine schlechtere Konvergenzrate".

allo
quelle