kontinuierliche Integration für wissenschaftliche Software

22

Ich bin kein Softwareentwickler. Ich bin Doktorandin auf dem Gebiet der Geowissenschaften.

Vor fast zwei Jahren habe ich angefangen, eine wissenschaftliche Software zu programmieren. Ich habe nie Continuous Integration (CI) verwendet, hauptsächlich, weil ich anfangs nicht wusste, dass es das gibt und ich die einzige Person war, die an dieser Software arbeitete.

Jetzt, da die Basis der Software läuft, interessieren sich andere dafür und möchten einen Beitrag zur Software leisten. Es ist geplant, dass andere Personen an anderen Universitäten Ergänzungen zur Kernsoftware implementieren. (Ich habe Angst, sie könnten Bugs einführen). Außerdem wurde die Software ziemlich komplex und es wurde immer schwieriger, sie zu testen, und ich plane auch, weiter daran zu arbeiten.

Aus diesen beiden Gründen denke ich jetzt immer mehr über die Verwendung von CI nach. Da ich nie eine Ausbildung zum Softwareingenieur hatte und niemand in meiner Umgebung jemals von CI gehört hat (wir sind Wissenschaftler, keine Programmierer), fällt es mir schwer, mit meinem Projekt zu beginnen.

Ich habe ein paar Fragen, bei denen ich mich beraten lassen möchte:

Zunächst eine kurze Erklärung der Funktionsweise der Software:

  • Die Software wird von einer XML-Datei gesteuert, die alle erforderlichen Einstellungen enthält. Sie starten die Software, indem Sie einfach den Pfad zur XML-Datei als Eingabeargument übergeben. Anschließend wird sie ausgeführt und erstellt einige Dateien mit den Ergebnissen. Ein einzelner Lauf kann ~ 30 Sekunden dauern.

  • Es ist eine wissenschaftliche Software. Fast alle Funktionen haben mehrere Eingabeparameter, deren Typen meist sehr komplexe Klassen sind. Ich habe mehrere TXT-Dateien mit großen Katalogen, die zum Erstellen von Instanzen dieser Klassen verwendet werden.

Kommen wir nun zu meinen Fragen:

  1. Unit-Tests, Integrationstests, End-to-End-Tests? : Meine Software besteht jetzt aus 30.000 Codezeilen mit Hunderten von Funktionen und ~ 80 Klassen. Es kommt mir irgendwie komisch vor, Unit-Tests für Hunderte von Funktionen zu schreiben, die bereits implementiert sind. Also habe ich darüber nachgedacht, einfach ein paar Testfälle zu erstellen. Bereiten Sie 10-20 verschiedene XML-Dateien vor und lassen Sie die Software laufen. Ich denke, das nennt man Ende-zu-Ende-Tests? Ich habe oft gelesen, dass Sie dies nicht tun sollten, aber vielleicht ist es für den Anfang in Ordnung, wenn Sie bereits eine funktionierende Software haben? Oder ist es einfach eine blöde Idee, CI zu einer bereits funktionierenden Software hinzuzufügen?

  2. Wie schreibt man Unit-Tests, wenn die Funktionsparameter schwer zu erstellen sind? Angenommen, ich habe eine Funktion double fun(vector<Class_A> a, vector<Class_B>)und müsste normalerweise zuerst mehrere Textdateien einlesen, um Objekte vom Typ Class_Aund zu erstellen Class_B. Ich dachte darüber nach, einige Dummy-Funktionen zu erstellen, Class_A create_dummy_object()ohne die Textdateien einzulesen. Ich dachte auch darüber nach, eine Art Serialisierung zu implementieren . (Ich plane nicht, die Erstellung der Klassenobjekte zu testen, da sie nur von mehreren Textdateien abhängen.)

  3. Wie schreibe ich Tests, wenn die Ergebnisse sehr unterschiedlich sind? Meine Software verwendet große Monte-Carlo-Simulationen und arbeitet iterativ. Normalerweise haben Sie ~ 1000 Iterationen und bei jeder Iteration erstellen Sie ~ 500-20.000 Instanzen von Objekten, die auf Monte-Carlo-Simulationen basieren. Wenn nur ein Ergebnis einer Iteration ein bisschen anders ist, sind die gesamten anstehenden Iterationen völlig anders. Wie gehen Sie mit dieser Situation um? Ich denke, das ist ein großer Punkt gegen End-to-End-Tests, da das Endergebnis sehr variabel ist?

Alle anderen Ratschläge von CI werden sehr geschätzt.

user7431005
quelle
1
Woher wissen Sie, dass Ihre Software richtig funktioniert? Können Sie eine Möglichkeit finden, diese Überprüfung zu automatisieren, damit Sie sie bei jeder Änderung ausführen können? Dies sollte Ihr erster Schritt sein, wenn Sie CI in ein bestehendes Projekt einführen.
Bart van Ingen Schenau
Wie haben Sie sichergestellt, dass Ihre Software überhaupt akzeptable Ergebnisse liefert? Was macht Sie sicher, dass es tatsächlich "funktioniert"? Die Antworten auf beide Fragen bieten Ihnen ausreichend Material, um Ihre Software jetzt und in Zukunft zu testen.
Polygnome

Antworten:

23

Das Testen von wissenschaftlicher Software ist schwierig, sowohl wegen des komplexen Themas als auch wegen der typischen wissenschaftlichen Entwicklungsprozesse (auch bekannt als "Hack", bis es funktioniert, was normalerweise nicht zu einem testbaren Design führt). Das ist ein bisschen ironisch, wenn man bedenkt, dass Wissenschaft reproduzierbar sein sollte. Was sich im Vergleich zu „normaler“ Software ändert, ist nicht, ob Tests sinnvoll sind (ja!), Sondern welche Arten von Tests angemessen sind.

Umgang mit Zufälligkeiten: Alle Läufe Ihrer Software MÜSSEN reproduzierbar sein. Wenn Sie Monte-Carlo-Techniken verwenden, müssen Sie es ermöglichen, einen bestimmten Startwert für den Zufallszahlengenerator bereitzustellen.

  • Dies kann leicht vergessen werden, wenn beispielsweise rand()die vom globalen Status abhängige Funktion von C verwendet wird .
  • Idealerweise wird ein Zufallszahlengenerator als explizites Objekt durch Ihre Funktionen übergeben. Der randomStandard-Bibliotheksheader von C ++ 11 erleichtert dies erheblich.
  • Anstatt den Zufallsstatus über Module der Software hinweg zu teilen, habe ich es für nützlich befunden, einen zweiten RNG zu erstellen, der durch eine Zufallszahl aus dem ersten RNG geimpft wird. Wenn sich dann die Anzahl der Anforderungen an den RNG durch das andere Modul ändert, bleibt die vom ersten RNG erzeugte Reihenfolge gleich.

Integrationstests sind vollkommen in Ordnung. Mit ihnen können Sie überprüfen, ob verschiedene Teile Ihrer Software korrekt zusammenspielen, und konkrete Szenarien ausführen.

  • Als Mindestqualitätsstufe kann „es stürzt nicht ab“ bereits ein gutes Testergebnis sein.
  • Um bessere Ergebnisse zu erzielen, müssen Sie die Ergebnisse auch mit einer Basislinie vergleichen. Diese Überprüfungen müssen jedoch etwas tolerant sein, z. B. um Rundungsfehler zu berücksichtigen. Es kann auch hilfreich sein, Auswertungsstatistiken anstelle von vollständigen Datenzeilen zu vergleichen.
  • Wenn die Überprüfung anhand einer Baseline zu anfällig wäre, überprüfen Sie, ob die Ausgaben gültig sind und einige allgemeine Eigenschaften erfüllen. Dies können allgemeine („ausgewählte Standorte müssen mindestens 2 km voneinander entfernt sein“) oder szenariospezifische Informationen sein, z. B. „ein ausgewählter Standort muss sich in diesem Bereich befinden“.

Bei der Ausführung von Integrationstests empfiehlt es sich, einen Test-Runner als separates Programm oder Skript zu schreiben. Dieser Test-Runner führt die erforderlichen Setups durch, führt die zu testende ausführbare Datei aus, überprüft die Ergebnisse und bereinigt sie anschließend.

Unit-Test- Style-Checks lassen sich nur schwer in wissenschaftliche Software einfügen, da die Software dafür nicht entwickelt wurde. Insbesondere Unit-Tests werden schwierig, wenn das zu testende System viele externe Abhängigkeiten / Wechselwirkungen aufweist. Wenn die Software nicht rein objektorientiert ist, ist es im Allgemeinen nicht möglich, diese Abhängigkeiten zu verspotten. Ich habe festgestellt, dass es am besten ist, Unit-Tests für solche Software weitgehend zu vermeiden, mit Ausnahme von reinen mathematischen Funktionen und Utility-Funktionen.

Sogar ein paar Tests sind besser als keine Tests. In Kombination mit dem Häkchen „es muss kompiliert werden“ ist dies bereits ein guter Start in die kontinuierliche Integration. Sie können später jederzeit wiederkommen und weitere Tests hinzufügen. Sie können dann Bereiche des Codes priorisieren, die mit größerer Wahrscheinlichkeit fehlerhaft sind, z. B. weil sie mehr Entwicklungsaktivität erhalten. Um festzustellen, welche Teile Ihres Codes nicht durch Unit-Tests abgedeckt sind, können Sie Tools zur Codeabdeckung verwenden.

Manuelles Testen: Insbesondere bei komplexen Problembereichen können Sie nicht alles automatisch testen. ZB arbeite ich gerade an einem stochastischen Suchproblem. Wenn ich teste, dass meine Software immer das gleiche Ergebnis liefert , kann ich sie nicht verbessern, ohne die Tests zu unterbrechen. Stattdessen habe ich es einfacher gemacht, manuelle Tests durchzuführen: Ich starte die Software mit einem festen Startwert und erhalte eine Visualisierungdes Ergebnisses (abhängig von Ihren Vorlieben machen es R, Python / Pyplot und Matlab einfach, qualitativ hochwertige Visualisierungen Ihrer Datensätze zu erhalten). Ich kann diese Visualisierung verwenden, um zu überprüfen, dass nichts furchtbar schief gelaufen ist. Ebenso kann die Verfolgung des Fortschritts Ihrer Software über die Protokollausgabe eine praktikable manuelle Testmethode sein, zumindest wenn ich die Art der zu protokollierenden Ereignisse auswählen kann.

amon
quelle
7

Es kommt mir irgendwie komisch vor, Unit-Tests für Hunderte von Funktionen zu schreiben, die bereits implementiert sind.

Sie möchten (normalerweise) die Tests schreiben, wenn Sie die genannten Funktionen ändern . Sie müssen sich nicht zurücklehnen und Hunderte von Komponententests für die vorhandenen Funktionen schreiben, das wäre (größtenteils) Zeitverschwendung. Die Software funktioniert (wahrscheinlich) so wie sie ist. Mit diesen Tests soll sichergestellt werden , dass zukünftige Änderungen nicht das alte Verhalten beeinträchtigen. Wenn Sie eine bestimmte Funktion nie wieder ändern, wird es sich wahrscheinlich nie lohnen, sich die Zeit zum Testen zu nehmen (da sie derzeit funktioniert, immer funktioniert hat und wahrscheinlich auch weiterhin funktioniert). Ich empfehle, Effektiv mit Legacy-Code zu arbeitenvon Michael Feathers auf dieser Vorderseite. Er verfügt über einige großartige allgemeine Strategien zum Testen bereits vorhandener Dinge, darunter Techniken zum Aufbrechen von Abhängigkeiten, Charakterisierungstests (Ausgabe der Copy / Paste-Funktion in die Testsuite, um sicherzustellen, dass Sie das Regressionsverhalten beibehalten) und vieles mehr.

Wie schreibt man Unit-Tests, wenn die Funktionsparameter schwer zu erstellen sind?

Idealerweise nicht. Stattdessen vereinfachen Sie das Erstellen der Parameter (und damit das Testen Ihres Designs). Zugegeben, Designänderungen brauchen Zeit, und diese Überarbeitungen können bei älteren Projekten wie Ihrem schwierig sein. TDD (Test Driven Development) kann dabei helfen. Wenn die Parameter sehr schwer zu erstellen sind, haben Sie viele Probleme, Tests im Test-First-Stil zu schreiben.

Verwenden Sie auf kurze Sicht Mocks, aber hüten Sie sich davor, die Hölle und die damit verbundenen Probleme auf lange Sicht zu verspotten. Als ich als Softwareentwickler aufgewachsen bin, habe ich festgestellt, dass Mocks fast immer ein Mini-Geruch sind, der versucht, ein größeres Problem zu lösen und das Kernproblem nicht anzugehen. Ich bezeichne es gerne als "Turd Wrapping", denn wenn Sie ein Stück Alufolie auf ein Stück Hundekot auf Ihren Teppich legen, stinkt es immer noch. Was Sie tun müssen, ist tatsächlich aufzustehen, die Kacke zu schöpfen, sie in den Müll zu werfen und dann den Müll herauszunehmen. Dies ist offensichtlich mehr Arbeit, und Sie riskieren, Fäkalien in Ihre Hände zu bekommen, die auf lange Sicht jedoch besser für Sie und Ihre Gesundheit sind. Wenn Sie diese Sachen einfach weiter einpacken, werden Sie nicht mehr lange in Ihrem Haus leben wollen. Mocks sind von Natur aus ähnlich.

Wenn Sie zum Beispiel eine Class_ADatei haben , die sich nur schwer instanziieren lässt, weil Sie 700 Dateien einlesen müssen, können Sie sie einfach verspotten. Das nächste , was Sie wissen, wird Ihre mock mehr aktuell, und die reale Class_A tut etwas wild anders als die Mock und Ihre Tests sind vorbei noch , obwohl sie sollten versagt werden. Eine bessere Lösung besteht darin, die Komponenten Class_Ain einfacher zu verwendende / zu testende Komponenten aufzuteilen und stattdessen diese Komponenten zu testen. Schreiben Sie vielleicht einen Integrationstest, der tatsächlich auf die Festplatte trifft, und stellen Sie sicher, dass er Class_Aals Ganzes funktioniert. Oder haben Class_ASie einfach einen Konstruktor dafür , den Sie mit einer einfachen Zeichenfolge (die Ihre Daten darstellt) instanziieren können, anstatt von der Festplatte lesen zu müssen.

Wie schreibe ich Tests, wenn die Ergebnisse sehr unterschiedlich sind?

Ein paar Tipps:

1) Verwenden Sie Inverse (oder allgemeiner Eigenschafts-basierte Tests). Was ist das FFT von [1,2,3,4,5]? Keine Ahnung. Was ist ifft(fft([1,2,3,4,5]))? Sollte sein [1,2,3,4,5](oder in der Nähe davon, Gleitkommafehler könnten auftreten).

2) Verwenden Sie "bekannte" Aussagen. Wenn Sie eine Determinantenfunktion schreiben, kann es schwierig sein, die Determinante einer 100x100-Matrix zu bestimmen. Aber Sie wissen, dass die Determinante der Identitätsmatrix 1 ist, auch wenn sie 100x100 ist. Sie wissen auch, dass die Funktion 0 in einer nicht invertierbaren Matrix zurückgeben sollte (wie eine 100x100, die mit allen 0en gefüllt ist).

3) Verwenden Sie grobe Aussagen anstelle exakter Aussagen. Ich habe vor einiger Zeit einen Code geschrieben, der zwei Bilder registriert, indem Verbindungspunkte generiert wurden, die eine Zuordnung zwischen den Bildern herstellen und eine Verzerrung zwischen ihnen ausführen, um eine Übereinstimmung zu erzielen. Es könnte sich auf einer Subpixel-Ebene registrieren. Wie kannst du es testen? Dinge wie:

EXPECT_TRUE(reg(img1, img2).size() < min(img1.size(), img2.size()))

Da Sie nur überlappende Teile registrieren können, muss das registrierte Bild kleiner oder gleich Ihrem kleinsten Bild sein.

scale = 255
EXPECT_PIXEL_EQ_WITH_TOLERANCE(reg(img, img), img, .05*scale)

Da ein für sich selbst registriertes Bild für sich selbst nahe sein sollte, es jedoch aufgrund des vorliegenden Algorithmus zu etwas mehr als Gleitkommafehlern kommen kann, prüfen Sie einfach, ob jedes Pixel mit +/- 5% des gültigen Bereichs (0-255) übereinstimmt ist ein gebräuchlicher Bereich (Graustufen). Sollte mindestens gleich groß sein. Sie können sogar einfach einen Rauchtest durchführen (dh es aufrufen und sicherstellen, dass es nicht abstürzt). Im Allgemeinen ist diese Technik besser für größere Tests, bei denen das Endergebnis nicht (leicht) vor dem Ausführen des Tests berechnet werden kann.

4) Verwenden Sie OR STORE einen Zufallszahlen-Startwert für Ihren RNG.

Läuft sie müssen reproduzierbar sein. Es ist jedoch falsch, dass die einzige Möglichkeit, einen reproduzierbaren Lauf zu erhalten, darin besteht , einem Zufallszahlengenerator einen bestimmten Startwert bereitzustellen . Manchmal sind Zufallstests wertvoll . Ich habe Fehler in wissenschaftlichem Code gesehen, die in entarteten Fällen auftreten, die zufällig generiert wurden. Statt immer Ihre Funktion mit dem gleichen Samen nennen, erzeugen einen zufälligen Samen, und dann , dass das Saatgut verwenden, und melden Sie sich den Wert des Samens. Auf diese Weise hat jeder Lauf einen anderen zufälligen Startwert. Wenn jedoch ein Absturz auftritt, können Sie das Ergebnis erneut ausführen, indem Sie den Startwert verwenden, den Sie zum Debuggen angemeldet haben. Ich habe dies tatsächlich in der Praxis verwendet und es hat einen Fehler beseitigt, also dachte ich, ich würde es erwähnen.Nachteil: Sie müssen Ihre Testläufe protokollieren. Oberseite: Korrektheit und Bugnuking.

HTH.

Matt Messersmith
quelle
2
  1. Arten von Tests

    • Es kommt mir irgendwie komisch vor, Unit-Tests für Hunderte von Funktionen zu schreiben, die bereits implementiert sind

      Stellen Sie sich das anders herum vor: Wenn ein Patch, der mehrere Funktionen berührt, einen Ihrer End-to-End-Tests durchbricht, wie können Sie dann herausfinden, welches Problem das ist?

      Unit-Tests für einzelne Funktionen lassen sich viel einfacher schreiben als für das gesamte Programm. Es ist viel einfacher , sicherzugehen, dass Sie eine einzelne Funktion gut abdecken. Es ist viel einfacher, eine Funktion umzugestalten, wenn Sie sicher sind, dass die Komponententests alle Eckfälle aufdecken, die Sie gebrochen haben.

      Unit-Tests für bereits vorhandene Funktionen zu schreiben, ist für jeden, der an einer älteren Codebasis gearbeitet hat, völlig normal. Sie sind eine gute Möglichkeit, Ihr Verständnis der Funktionen zu bestätigen, und, wenn sie einmal geschrieben wurden, eine gute Möglichkeit, unerwartete Verhaltensänderungen zu finden.

    • Auch End-to-End-Tests lohnen sich. Wenn es einfacher ist, sie zu schreiben, sollten Sie dies unbedingt zuerst tun und ad-hoc Unit-Tests hinzufügen, um die Funktionen abzudecken, die Sie am meisten fürchten, wenn andere kaputt gehen. Sie müssen nicht alles auf einmal tun.

    • Ja, das Hinzufügen von CI zu vorhandener Software ist sinnvoll und normal.

  2. So schreiben Sie Komponententests

    Wenn Ihre Objekte sehr teuer und / oder komplex sind, schreiben Sie Verspottungen. Sie können die Tests einfach mit Mocks getrennt von den Tests mit realen Objekten verknüpfen, anstatt Polymorphismus zu verwenden.

    Sie sollten auf jeden Fall eine einfache Möglichkeit zum Erstellen von Instanzen haben - eine Funktion zum Erstellen von Dummy-Instanzen ist üblich -, aber es ist auch sinnvoll, Tests für den tatsächlichen Erstellungsprozess durchzuführen.

  3. Variable Ergebnisse

    Sie müssen einige Invarianten für das Ergebnis haben. Testen Sie diese Werte anstatt eines einzelnen numerischen Werts.

    Sie könnten einen Schein-Pseudozufallszahlengenerator bereitstellen, wenn Ihr Monte-Carlo-Code diesen als Parameter akzeptiert, wodurch die Ergebnisse zumindest für einen bekannten Algorithmus vorhersehbar werden, aber er ist spröde, sofern er nicht jedes Mal buchstäblich dieselbe Zahl zurückgibt.

Nutzlos
quelle
1
  1. Es ist nie eine blöde Idee, CI hinzuzufügen. Aus Erfahrung weiß ich, dass dies der richtige Weg ist, wenn Sie ein Open-Source-Projekt haben, bei dem die Menschen frei sind, Beiträge zu leisten. Mit CI können Sie verhindern, dass Benutzer Code hinzufügen oder ändern, wenn der Code Ihr Programm beschädigt, sodass er für eine funktionierende Codebasis von nahezu unschätzbarem Wert ist.

    Wenn Sie Tests in Betracht ziehen, können Sie auf jeden Fall einige End-to-End-Tests bereitstellen (ich denke, es ist eine Unterkategorie von Integrationstests), um sicherzustellen, dass Ihr Code-Fluss so funktioniert, wie er sollte. Sie sollten mindestens einige grundlegende Unit-Tests bereitstellen, um sicherzustellen, dass die Funktionen die richtigen Werte ausgeben, da ein Teil der Integrationstests andere Fehler ausgleichen kann, die während des Tests gemacht wurden.

  2. Die Erstellung von Testobjekten ist in der Tat ziemlich schwierig und mühsam. Sie haben Recht, wenn Sie Dummy-Objekte erstellen möchten. Diese Objekte sollten einige Standardwerte haben, aber Kantenwerte, für die Sie mit Sicherheit wissen, wie die Ausgabe aussehen soll.

  3. Das Problem bei Büchern zu diesem Thema ist, dass sich die Landschaft von CI (und anderen Teilen von Devops) so schnell entwickelt, dass alles in einem Buch wahrscheinlich einige Monate später veraltet sein wird. Ich kenne keine Bücher, die Ihnen helfen könnten, aber Google sollte wie immer Ihr Retter sein.

  4. Sie sollten Ihre Tests mehrmals selbst durchführen und statistische Analysen durchführen. Auf diese Weise können Sie einige Testfälle implementieren, bei denen Sie den Median / Durchschnitt mehrerer Läufe verwenden und mit Ihrer Analyse vergleichen, um festzustellen, welche Werte korrekt sind.

Einige Hinweise:

  • Verwenden Sie die Integration von CI-Tools in Ihre GIT-Plattform, um zu verhindern, dass beschädigter Code in Ihre Codebasis eingeht.
  • Beenden Sie das Zusammenführen von Code, bevor andere Entwickler Peer-Reviews durchgeführt haben. Dies macht Fehler leichter bekannt und verhindert erneut, dass fehlerhafter Code in Ihre Codebasis eingeht.
Pelikan
quelle
1

In einer Antwort vor amon wurden bereits einige sehr wichtige Punkte erwähnt. Lassen Sie mich noch etwas hinzufügen:

1. Unterschiede zwischen der Entwicklung von wissenschaftlicher Software und kommerzieller Software

Bei wissenschaftlicher Software liegt der Schwerpunkt normalerweise natürlich auf dem wissenschaftlichen Problem. Die Probleme bestehen eher darin, den theoretischen Hintergrund zu behandeln, die beste numerische Methode zu finden usw. Die Software ist nur ein, mehr oder weniger kleiner Teil der Arbeit.

Die Software wird in den meisten Fällen von einer oder wenigen Personen geschrieben. Es wird oft für ein bestimmtes Projekt geschrieben. Wenn das Projekt abgeschlossen und alles veröffentlicht ist, wird die Software in vielen Fällen nicht mehr benötigt.

Kommerzielle Software wird normalerweise von großen Teams über einen längeren Zeitraum entwickelt. Dies erfordert viel Planung für Architektur, Design, Komponententests, Integrationstests usw. Diese Planung erfordert viel Zeit und Erfahrung. In einem wissenschaftlichen Umfeld gibt es normalerweise keine Zeit dafür.

Wenn Sie Ihr Projekt in eine Software konvertieren möchten, die kommerzieller Software ähnelt, sollten Sie Folgendes überprüfen:

  • Haben Sie Zeit und Ressourcen?
  • Was ist die langfristige Perspektive der Software? Was passiert mit der Software, wenn Sie Ihre Arbeit beenden und die Universität verlassen?

2. End-to-End-Tests

Wenn die Software immer komplexer wird und mehrere Personen daran arbeiten, sind Tests obligatorisch. Aber wie Amon bereits erwähnt, das Hinzufügen von Unit - Tests für die wissenschaftliche Software ist ziemlich schwierig. Sie müssen also einen anderen Ansatz verwenden.

Da Ihre Software, wie die meisten wissenschaftlichen Programme, ihre Eingaben aus einer Datei erhält, ist sie perfekt für die Erstellung mehrerer Beispiel-Eingabe- und Ausgabedateien geeignet. Sie sollten diese Tests bei jedem Release automatisch ausführen und die Ergebnisse mit Ihren Proben vergleichen. Dies könnte ein sehr guter Ersatz für Unit-Tests sein. Auf diese Weise erhalten Sie auch Integrationstests.

Um reproduzierbare Ergebnisse zu erzielen, sollten Sie für Ihren Zufallsgenerator natürlich den gleichen Startwert verwenden , wie amon bereits geschrieben hat.

Die Beispiele sollten typische Ergebnisse Ihrer Software abdecken. Dies sollte auch Randfälle Ihres Parameterraums und numerische Algorithmen einschließen.

Sie sollten versuchen, Beispiele zu finden, die nicht zu viel Zeit zum Ausführen benötigen, aber dennoch typische Testfälle abdecken.

3. Kontinuierliche Integration

Da das Ausführen der Testbeispiele einige Zeit in Anspruch nehmen kann, halte ich eine kontinuierliche Integration für nicht durchführbar. Sie müssen die zusätzlichen Teile wahrscheinlich mit Ihren Kollegen besprechen. Zum Beispiel müssen sie mit den verwendeten numerischen Methoden übereinstimmen.

Daher halte ich es für besser, die Integration nach Erörterung des theoretischen Hintergrunds und der numerischen Methoden, sorgfältigen Tests usw. in einer genau definierten Weise durchzuführen.

Ich halte es nicht für eine gute Idee, eine Art Automatismus für die kontinuierliche Integration zu haben.

Verwenden Sie übrigens ein Versionskontrollsystem?

4. Testen Sie Ihre numerischen Algorithmen

Wenn Sie numerische Ergebnisse vergleichen, z. B. bei der Überprüfung Ihrer Testausgaben, sollten Sie keine Gleitkommazahlen auf Gleichheit prüfen. Es kann immer zu Rundungsfehlern kommen. Überprüfen Sie stattdessen, ob die Differenz unter einem bestimmten Schwellenwert liegt.

Es ist auch eine gute Idee, Ihre Algorithmen mit anderen Algorithmen zu vergleichen oder das wissenschaftliche Problem auf andere Weise zu formulieren und die Ergebnisse zu vergleichen. Wenn Sie die gleichen Ergebnisse auf zwei oder mehr unabhängige Arten erhalten, ist dies ein guter Hinweis darauf, dass Ihre Theorie und Ihre Implementierung korrekt sind.

Sie können diese Tests in Ihrem Testcode durchführen und den schnellsten Algorithmus für Ihren Produktionscode verwenden.

Bernie
quelle
0

Mein Rat wäre, sorgfältig zu wählen, wie Sie Ihre Bemühungen ausgeben. In meinem Bereich (Bioinformatik) ändern sich die Algorithmen nach dem neuesten Stand der Technik so schnell, dass der Aufwand für die Fehlerprüfung Ihres Codes möglicherweise besser für den Algorithmus selbst aufgewendet werden kann.

Das heißt, was geschätzt wird, ist:

  • ist es die beste Methode zur Zeit in Bezug auf den Algorithmus?
  • Wie einfach ist es, auf verschiedene Computerplattformen zu portieren (verschiedene HPC-Umgebungen, Betriebssystemvarianten usw.)?
  • Robustheit - Läuft sie auf MEINEM Datensatz?

Ihr Instinkt, eine kugelsichere Codebasis aufzubauen, ist großartig, aber es lohnt sich, daran zu denken, dass dies kein kommerzielles Produkt ist. Machen Sie es so portabel wie möglich, fehlerfrei (für Ihren Benutzertyp), bequem für andere, und konzentrieren Sie sich dann auf den Algorithmus selbst

Kugelfisch
quelle