Wie schreibe ich Unit-Tests für Legacy-Code (den ich nicht verstehe)?

8

Nach vorne

Ich habe viele Dinge gelesen, bevor ich diese Frage gestellt habe, einschließlich vieler relevanter Fragen hier auf SE:

Ich kann jedoch nicht anders, als das Gefühl zu haben, dass der Juckreiz noch nicht zerkratzt wurde, nachdem ich um Hilfe gelesen habe.


TL; DR

Wie schreibe ich Komponententests für Legacy-Code, den ich nicht ausführen, simulieren, lesen oder leicht verstehen kann? Welche Regressionstests sind für eine Komponente nützlich, die vermutlich wie beabsichtigt funktioniert?


Das ganze Bild

Ich bin wieder ein zurückkehrender Sommerpraktikant, als ich in die Graduiertenschule übergehe. Meine Aufgabe beinhaltet folgende Anforderungen:

  1. Bewerten Sie für ein bestimmtes Produkt, ob unser Softwareteam die IDE- und JUnit-Version aktualisieren kann, ohne die Kompatibilität mit den vorhandenen Projekten zu verlieren.
  2. Entwickeln Sie Komponententests für eine Komponente im vorhandenen Java-Code (es handelt sich größtenteils nicht um Java). Wir möchten das Software-Team davon überzeugen, dass Unit-Tests und TDD unschätzbare Werkzeuge sind, die sie verwenden sollten. (Derzeit besteht eine Codeabdeckung von 0%.)
  3. Beenden Sie irgendwie die Tage der Cowboy-Codierung für ein kritisches System.

Nachdem ich eine Kopie des Quellcodes erhalten hatte, habe ich versucht, ihn zu erstellen und auszuführen, damit ich verstehe, was dieses Produkt tut und wie es funktioniert. Ich konnte nicht. Ich habe meine Vorgesetzten gefragt, wie es mir geht, und mir wurde ein neuer eigenständiger Computer ausgestellt, der in der Lage ist, ihn zu erstellen, einschließlich der tatsächlich erstellten Build-Skripte. Das hat auch nicht funktioniert, da der Produktionscode erwartungsgemäß nur auf dem eingebetteten System ausgeführt wird, für das er entwickelt wurde. Da sie jedoch einen Simulator für diesen Zweck haben, haben sie den Simulator erhalten und ihn für mich auf diese Maschine gestellt. Der Simulator funktionierte auch nicht. Stattdessen erhielt ich endlich einen Ausdruck einer GUI für einen bestimmten Bildschirm. Sie haben auch keine Codekommentare innerhalb des über 700.000 Java LOC, was das Erfassen noch schwieriger macht. Außerdem, Es gab Probleme bei der Bewertung, ob ihre Projekte mit neueren IDEs kompatibel waren oder nicht. Insbesondere wurde ihr Code nicht richtig in die von ihnen verwendete IDE-Version geladen.

Mein Inventar sieht folgendermaßen aus:

  • NetBeans 8, 9, 10, 11
  • Einheit 4, 5
  • Ihr Quellcode für ein bestimmtes Produkt (enthält mehr als 700.000 Java LOC)
  • Praktisch keine Codekommentare (gelegentlich eine Signatur)
  • Keine vorhandenen Tests
  • Ein physisches Foto eines GUI-Fensters
  • Ein Software-Design-Dokument (109 S.), in dem die Komponente im Bild nicht behandelt wird

Ich habe zumindest genug, um theoretisch Tests zu schreiben, die ausgeführt werden können. Also habe ich einen grundlegenden Unit-Test für diese Komponente versucht. Ich konnte jedoch die Objekte, die es als Abhängigkeiten gab, nicht initialisieren, einschließlich Modelle, Manager und DB-Verbindungen. Ich habe nicht viel Erfahrung mit JUnit, abgesehen von grundlegenden Unit-Tests. Folgen Sie mir also zum nächsten Abschnitt.


Was ich aus meiner Lektüre gelernt habe

  1. Verspotten: Wenn ich einen Komponententest schreibe, müssen wahrscheinlich Scheinvariablen für Produktionsabhängigkeiten vorhanden sein, in denen ich nicht einfach initialisieren kann setUp.
  2. Jeder hier schlägt großzügig das Buch "Effektiv mit Legacy-Code arbeiten" von Michael Feathers vor.
  3. Regressionstests sind wahrscheinlich ein guter Anfang. Ich glaube nicht, dass ich genug Waffen habe, um Integrationstests durchzuführen, und Regressionstests würden unser Softwareteam sofort befriedigen. Ich habe jedoch keinen Zugriff auf die bekannten Fehler. aber ich könnte möglicherweise fragen.

Und jetzt ein Versuch, die Unsicherheit, die ich noch habe, als Frage zu artikulieren. Im Wesentlichen verstehe ich nicht, wie das Schreiben dieser Tests funktioniert . Vorausgesetzt, ich erhalte (wahrscheinlich) keine weitere Anleitung von meinen Vorgesetzten, liegt es in meinem Umfeld, nicht nur zu lernen, was diese Komponente tut, sondern zu entscheiden, welche Tests tatsächlich als Regressionstests nützlich sind.

Können Sie als Fachleute, die länger als ich mit solchen Projekten gearbeitet haben, Anleitungen zum Schreiben von Komponententests in solchen Situationen geben?

Joshua Detwiler
quelle
12
How do I write unit tests for legacy code that I can't build, run, simulate, read about, or otherwise understand?- Das kannst du nicht. Sie müssen zumindest wissen, wie hoch die erwartete Ausgabe für eine bestimmte Eingabe ist.
Robert Harvey
1
Hast du das Buch von Michael Feathers schon gelesen?
Robert Harvey
@ RobertHarvey Es ist eine leichte Übertreibung. Natürlich kann ich mir die Zeit nehmen, Code zu lesen, um ihn zu verstehen, aber das ist normalerweise der letzte Ausweg für etwas so Großes. Und nein, habe ich noch nicht, seit ich das Buch heute Morgen gefunden habe.
Joshua Detwiler
Wenn Sie Legacy-Code haben, den Sie nicht einmal erstellen können, sind Sie sicher, dass Sie überhaupt die richtige Version des Codes haben? Sie müssen dies zuerst beheben, bevor Sie sich Gedanken über automatisierte Tests machen. Ist der Code, den Sie erstellen möchten, mit dem Code identisch, den Ihre Benutzer ausführen? Der erste Schritt bei alledem wäre, wenn Sie ein laufendes und funktionierendes System hätten, zu versuchen, es aus Sicht des Benutzers zu verstehen. es kann helfen, Benutzerdokumentation zu lesen und möglicherweise Benutzerdaten zu betrachten, wenn möglich, um herauszufinden, was es tut
Ben Cottrell
2
Wenn Sensordaten für die Ausführung von entscheidender Bedeutung sind, scheint es ein guter Anfang zu sein, die Sensoren (oder was auch immer die externen Abhängigkeiten sind) zu verspotten.
JimmyJames

Antworten:

10

In erster Näherung sind die Stakeholder einer Testsuite die Codeentwickler / -betreuer. Du wirst etwas Zeit brauchen. Bestehen Sie darauf.

Fragen Sie sie nach Problemen, mit denen sie konfrontiert sind.
Fragen Sie sie nach Fehlern, die sie kürzlich behoben haben (in den letzten Jahren, vorausgesetzt, dies ist ein langes, langsames Projekt).

Ich habe den Eindruck, dass Sie nicht erwarten, dass sie für Ihre Arbeit liebenswürdig sind. Vielleicht hast du Recht. Aber ich glaube nicht, dass man ohne ihre Hilfe etwas Nützliches bauen kann.

Sie werden Ihre Hand halten, indem Sie den ersten Test schreiben. Vielleicht ziehst du sie, aber du hältst Hände in beide Richtungen.

Sobald Sie einen Test haben, wird hoffentlich klarer, wie der zweite zu schreiben ist. Wenn Sie es geschafft haben, irgendeine Art von Beziehung aufzubauen, wird es sicherlich klarer.

ShapeOfMatter
quelle
Danke für die Antwort! Könnten Sie sich auch die zweite Hälfte meiner Frage ansehen? Meine erste Hälfte bestand aus einer kleinen Vervielfältigung der Fragen, die ich verlinkt hatte, und aus diesem Grund interessierte mich hauptsächlich der andere Teil meiner Frage.
Joshua Detwiler
Ich weiß nicht, wie hilfreich ich sein kann. Haben Sie Probleme zu verstehen, wie das Testframework funktioniert? Fragen Sie sich, was Sie testen sollen? Regressionstests sind dafür eine gute Wahl: Welcher Test hätte verhindert, dass dieser Fehler, den wir gerade behoben haben, verschoben wird, wenn wir so vorausschauend gewesen wären .
ShapeOfMatter
Es war mehr "Welche Regressionstests sind für eine Komponente nützlich, die vermutlich wie beabsichtigt funktioniert?" Wenn ich nützliche Tests erstellen möchte, was nützt es dann, völlig blind zu sein, was funktioniert, was nicht und wie es überhaupt funktioniert? Ich existiere in einem Vakuum, abgesehen vom Software-Team, und die Leute, die mir diese Aufgabe gegeben haben, existieren in einem Vakuum, das von uns beiden getrennt ist.
Joshua Detwiler
Ich bin mir immer noch sicher, dass Sie darauf bestehen müssen, im selben Raum wie die Leute zu sein, die die Software schreiben, idealerweise für ein paar Tage und wahrscheinlich mehr als einmal. Ich kann mich auf auferlegte absurde Arbeitssituationen beziehen, aber irgendwann muss man entweder seinen Job riskieren oder akzeptieren, dass man nur ein
Bankwärmer ist
In Bezug auf Regressionstests: Es unterscheidet sich konzeptionell nicht von anderen Tests: Was soll das Programm tun (oder nicht tun), bevor ich gegenüber einem Kollegen behauptete, dass es funktioniert? Wenn es Aufzeichnungen über neue Funktionen oder kürzlich aufgetretene Fehler gibt, sind dies gute Ausgangspunkte. Oder schauen Sie sich einfach die Dokumentation an und wählen Sie etwas aus, das testbar aussieht. Java hat Funktionen eingegeben, was gut ist. Eine eindeutige Typensignatur kann Ihnen viel darüber sagen, was Sie von einer Funktion erwarten können (und kann als eine Art Komponententest an sich betrachtet werden). Das Überprüfen des Verhaltens von NULL / leer-Zeichenfolge / max-int kann ebenfalls gut sein.
ShapeOfMatter
3

Ich gehe davon aus, dass Sie den Code irgendwann zum Kompilieren bringen können. Wenn Sie nicht einmal so weit kommen können, sind Sie auf der Suche nach einem Narren.


Das Fehlen geeigneter Anforderungen, Spezifikationen oder Screenshots ist kein Blocker für das Schreiben von Tests. Solange Sie den Quellcode lesen können, können Sie Tests schreiben.

Wenn Sie die Berechtigung erhalten, die Codebasis umzugestalten, um beispielsweise Datenbankverbindungen hinter ihrer eigenen Schnittstelle zu isolieren, können Sie einige Black-Box-Komponententests schreiben. Schreiben Sie im Grunde Tests, um Eingaben in eine Methode zu werfen und deren Verhalten oder Ausgabe zu bestätigen. Holen Sie sich Tests für jede Codezeile in einer Methode und lassen Sie dann eines der leitenden Mitglieder des Teams Ihre Tests überprüfen.

Wenn Sie nicht die Berechtigung erhalten, die Codebasis für das Schreiben von Komponententests umzugestalten, sind vollständige Integrationstests oder UI-Automatisierungstests Ihre einzige Option. Selbst dann ist Black-Box-Testen Ihre beste Strategie - werfen Sie einige Eingaben auf die Benutzeroberfläche und sehen Sie, wie sie reagiert. Machen Sie Ihre Behauptungen. Lassen Sie Ihre Tests von einem hochrangigen Teammitglied überprüfen.

Irgendwann werden Sie über genügend automatisierte Tests verfügen, damit Sie mit der Umgestaltung der Codebasis beginnen können, um Unit-Tests einzuführen. Die UI-Tests stellen sicher, dass die wichtigsten Anwendungsfälle für Unternehmen funktionieren. Anschließend können Sie eine Architektur nachrüsten, die für Tests auf Einheiten- oder Komponentenebene geeignet ist.

Ein weiterer Vorteil von UI-Tests besteht darin, dass Sie bei Ihrem Team den Ruf aufbauen können, dass Sie die Codebasis verstehen, was sie wiederum offener für Änderungen macht, da der Beweis im Pudding liegt. Und Sie haben Pudding gemacht, indem Sie bestandene Tests geschrieben haben.

Zusamenfassend:

  • Schreiben Sie Black-Box-Tests als Einheits- oder automatisierte UI-Tests
  • Lassen Sie hochrangige Mitglieder Ihre Tests überprüfen

Sie werden überrascht sein, wie schnell Sie die Gesamtansicht einer 700.000-Zeilen-Anwendung erlernen können

Greg Burghardt
quelle
1

Basierend auf der Beschreibung des Problems und Ihren Kommentaren denke ich, dass das Beste, was Sie tun können, darin besteht, mit der Java-API zu beginnen und zu versuchen, einen einzelnen Komponententest um eine isolierte Methode herum zu erstellen.

Ohne Zugriff auf den Code kann ich Ihnen nur eine eingeschränkte Anleitung geben, aber ich würde nach etwas suchen, das darin a) keine Abhängigkeiten aufweist, b) keine Statusänderungen vornimmt. Zum Beispiel. Angenommen, es gibt eine Methode, die einen Wert annimmt und überprüft, ob er in einen bestimmten Bereich fällt. Wenn Sie etwas ohne Abhängigkeiten nicht finden können, versuchen Sie etwas, das einen Wert aus einer Abhängigkeit abruft, und versuchen Sie, ihn zu verspotten.

Sobald Sie so etwas Kleines gefunden haben, können Sie mit dem Erstellen von Tests beginnen. Wenn die Methode einen positiven Wert testet, übergeben Sie einen negativen Wert und stellen Sie sicher, dass er ihn abfängt usw. Das Problem hierbei ist, dass Sie möglicherweise nicht genau wissen, wie das richtige Verhalten ist. Möglicherweise müssen Sie um Hilfe bitten oder die Dokumentation dafür durchforsten.

Es ist unwahrscheinlich, dass Sie damit sehr weit kommen. Die Realität ist, dass das Schreiben von Code, damit er einem Unit-Test unterzogen werden kann, eine Kunst für sich ist. Code, der nicht in diesem Sinne geschrieben wurde, kann für Unit-Tests schwierig bis unmöglich sein.

Eine weitere Option, die möglicherweise einfacher implementiert werden kann, ist das Testen der Binärkompatibilität. Das heißt, Sie erfassen die Ein- und Ausgänge eines Systems und geben dann zum Testen dieselben Ein- und Ausgänge ein und vergleichen die Ausgänge. Dies sagt Ihnen nicht, ob der Code zu Beginn richtig oder falsch ist, kann jedoch dazu beitragen, Regressionsfehler zu erkennen, bei denen sich bei einer anderen Änderung unbeabsichtigt Änderungen ergeben haben. Sie müssen jedoch in der Lage sein, die gesamte Anwendung auszuführen, um dies zu erreichen.

JimmyJames
quelle