Beginnen wir mit einem Beispiel.
Nehmen wir an, ich habe eine Methode namens export
, die stark vom DB-Schema abhängt. Und mit „stark abhängig“ meine ich, dass das Hinzufügen einer neuen Spalte zu einer bestimmten Tabelle oft (sehr oft) zu einer entsprechenden export
Methodenänderung führt (normalerweise sollten Sie das neue Feld auch zu den Exportdaten hinzufügen).
Programmierer vergessen oft, die export
Methode zu ändern , da nicht klar ist, ob man sich das überhaupt ansehen sollte. Mein Ziel ist es, den Programmierer zu einer expliziten Entscheidung zu zwingen, um festzustellen, ob er die export
Methode vergessen hat oder einfach kein Feld zu den Exportdaten hinzufügen möchte. Und ich suche nach einer Designlösung für dieses Problem.
Ich habe zwei Ideen, aber beide haben Fehler.
Intelligenter Wrapper "Alles lesen"
Ich kann den Smart Wrapper erstellen, der sicherstellt, dass alle Daten explizit gelesen werden.
Etwas wie das:
def export():
checker = AllReadChecker.new(table_row)
name = checker.get('name')
surname = checker.get('surname')
checker.ignore('age') # explicitly ignore the "age" field
result = [name, surname] # or whatever
checker.check_now() # check all is read
return result
Bestätigt also, checker
wenn table_row
andere Felder enthalten, die nicht gelesen wurden. Aber all das sieht irgendwie schwer aus und beeinflusst (vielleicht) die Leistung.
„Überprüfen Sie, dass die Methode“ Unittest
Ich kann gerade das unittest verursachen, das sich das letzte Tabellenschema erinnert und jedes Mal fehlschlägt, wenn die Tabelle geändert wird. In diesem Fall würde der Programmierer so etwas wie "Vergiss nicht, die export
Methode zu überprüfen " sehen. Das Ausblenden der Warnung Programmierer würde (oder würde nicht - das ist ein Problem) auschecken export
und manuell (das ist ein anderes Problem) den Test beheben, indem neue Felder hinzugefügt werden.
Ich habe ein paar andere Ideen, aber sie sind zu mühsam umzusetzen oder zu schwer zu verstehen (und ich möchte nicht, dass das Projekt zu einem Rätsel wird).
Das obige Problem ist nur ein Beispiel für die größere Gruppe von Problemen, auf die ich von Zeit zu Zeit stoße. Ich möchte einige Teile des Codes und / oder der Infrastruktur binden, sodass das Ändern eines davon den Programmierer sofort darauf hinweist, einen anderen zu überprüfen. Normalerweise haben Sie einige einfache Tools wie das Extrahieren allgemeiner Logik oder das Schreiben zuverlässiger Unittest, aber ich suche das Tool für komplexere Fälle: Vielleicht sind mir einige Entwurfsmuster bekannt.
quelle
export
basierend auf dem Schema generieren ?export
alles vorhanden ist, was Sie wirklich benötigen?Antworten:
Sie sind mit Ihrer Unit-Test-Idee auf dem richtigen Weg, aber Ihre Implementierung ist falsch.
Wenn sich das
export
auf das Schema bezieht und das Schema geändert wurde, gibt es zwei mögliche Fälle:Entweder
export
funktioniert das noch einwandfrei, da es von einer leichten Änderung des Schemas unberührt blieb,Oder es bricht.
In beiden Fällen ist es das Ziel des Builds, diese mögliche Regression aufzuspüren. Eine Reihe von Tests - seien es Integrationstests oder Systemtests oder Funktionstests oder etwas anderes - stellen sicher, dass Ihre
export
Prozedur mit dem aktuellen Schema funktioniert , unabhängig davon, ob sie sich seit dem vorherigen Commit geändert hat oder nicht. Wenn diese Tests erfolgreich sind, ist das großartig. Wenn sie fehlschlagen, ist dies ein Zeichen für den Entwickler, dass er möglicherweise etwas verpasst hat, und ein klarer Hinweis darauf, wo er suchen muss.Warum ist Ihre Implementierung falsch? Aus mehreren Gründen.
Es hat nichts mit Unit-Tests zu tun ...
... und eigentlich ist es nicht einmal ein Test.
Das Schlimmste ist, dass das Beheben des „Tests“ tatsächlich ein Ändern des „Tests“ erfordert, dh eine Operation, die mit dem überhaupt nicht zusammenhängt
export
.Indem Sie tatsächliche Tests für die
export
Prozedur durchführen, stellen Sie stattdessen sicher, dass der Entwickler das Problem behebtexport
.Im Allgemeinen ist dies ein gutes Zeichen dafür, dass Sie bei einer Situation, in der eine Änderung in einer Klasse immer oder normalerweise eine Änderung in einer völlig anderen Klasse erforderlich macht, Ihr Design falsch gemacht haben und gegen das Prinzip der einheitlichen Verantwortung verstoßen.
Während ich speziell über Klassen spreche, gilt dies mehr oder weniger auch für andere Entitäten. Beispielsweise sollte eine Änderung des Datenbankschemas entweder automatisch in Ihrem Code widergespiegelt werden, beispielsweise durch Codegeneratoren, die von vielen ORMs verwendet werden, oder zumindest leicht zu lokalisieren sein: Wenn ich eine
Description
Spalte zurProduct
Tabelle hinzufüge und keine ORMs oder Codegeneratoren verwende, Ich erwarte zumindest eine einzelne Änderung innerhalb derData.Product
Klasse der DAL, ohne die gesamte Codebasis durchsuchen zu müssen und einige Vorkommen derProduct
Klasse beispielsweise in der Präsentationsebene zu finden.Wenn Sie die Änderung nicht vernünftigerweise auf einen Ort beschränken können (entweder weil es dort einfach nicht funktioniert oder weil es sehr viel Entwicklung erfordert), besteht das Risiko von Regressionen . Wenn ich die Klasse ändere
A
und die KlasseB
irgendwo in der Codebasis nicht mehr funktioniert, ist das eine Regression.Testen senkt das Risiko von Regressionen und zeigt Ihnen, was viel wichtiger ist, den Ort einer Regression. Wenn Sie wissen, dass Änderungen an einem Ort Probleme in einem ganz anderen Teil der Codebasis verursachen, sollten Sie daher sicherstellen, dass Sie über genügend Tests verfügen, die Alarme auslösen, sobald auf dieser Ebene eine Regression auftritt.
Verlassen Sie sich in solchen Fällen auf keinen Fall nur auf die Kommentare. Etwas wie:
funktioniert nie In den meisten Fällen wird es nicht nur von Entwicklern nicht gelesen, sondern oft auch entfernt oder weit von der betroffenen Zeile entfernt und ist nicht mehr zu verstehen.
quelle
If you change the following line...
, die automatisch funktioniert und nicht einfach ignoriert werden kann. Ich habe noch nie jemanden gesehen, der solche Fallen benutzt hat, daher der Zweifel.B
nicht aufhört zu arbeiten, sondern möglicherweise nicht mehr richtig funktioniert. Aber ich kann keine Zukunft vorhersagen und keine Tests schreiben, die die Zukunft vorhersagen. Deshalb versuche ich, den Entwickler daran zu erinnern, diesen neuen Test zu schreiben.Es hört sich für mich so an, als wären Ihre Änderungen nicht genau festgelegt. Angenommen, Sie wohnen an einem Ort ohne Postleitzahlen, sodass in der Adresstabelle keine Postleitzahlenspalte vorhanden ist. Dann werden Postleitzahlen eingeführt, oder Sie fangen an, sich mit Kunden zu befassen, die dort leben, wo Postleitzahlen existieren, und Sie müssen diese Spalte der Tabelle hinzufügen.
Wenn das Arbeitselement nur "Postleitzahlenspalte zur Adresstabelle hinzufügen" lautet, wird der Export abgebrochen oder es werden zumindest keine Postleitzahlen exportiert. Wie sieht es aber mit dem Eingabebildschirm für Postleitzahlen aus? Der Bericht, der alle Kunden und deren Adressen auflistet? Es gibt Unmengen von Dingen, die geändert werden müssen, wenn Sie diese Spalte hinzufügen. Das Erinnern an diese Dinge ist eine wichtige Aufgabe - Sie sollten sich nicht auf zufällige Code-Artefakte verlassen, um Entwickler zum Erinnern zu "verleiten".
Wenn die Entscheidung getroffen wird, eine aussagekräftige Spalte hinzuzufügen (d. H. Nicht nur eine zwischengespeicherte Gesamtsuche oder eine denormalisierte Suche oder einen anderen Wert, der nicht zu einem Export oder Bericht oder einem Eingabebildschirm gehört), sollten die erstellten Arbeitselemente ALLE Änderungen enthalten Erforderlich - Hinzufügen der Spalte, Aktualisieren des Füllskripts, Aktualisieren der Tests, Aktualisieren des Exports, der Berichte, der Eingabebildschirme usw. Diese dürfen nicht alle derselben Person zugewiesen (oder von ihr abgeholt) werden, aber sie müssen alle erledigt werden.
Manchmal entscheiden sich Entwickler, Spalten selbst hinzuzufügen, um größere Änderungen zu implementieren. Beispielsweise könnte jemand ein Arbeitselement geschrieben haben, um einem Eingabebildschirm und einem Bericht etwas hinzuzufügen, ohne darüber nachzudenken, wie es implementiert wird. Wenn dies häufig vorkommt, müssen Sie entscheiden, ob Ihr Workitem-Addierer Implementierungsdetails kennen muss (um alle richtigen Workitems hinzufügen zu können) oder ob Entwickler sich bewusst sein müssen, dass das Workitem- Addierer lässt manchmal Dinge aus. Wenn es das letztere ist, brauchen Sie eine Kultur von "Ändern Sie nicht nur das Schema, sondern denken Sie darüber nach, was sich sonst noch darauf auswirkt."
Wenn es viele Entwickler gäbe und dies mehr als einmal passieren würde, würde ich eine Check-in-Benachrichtigung für den Teamleiter oder eine andere leitende Person einrichten, um über Schemaänderungen informiert zu werden. Diese Person könnte dann nach verwandten Arbeitselementen suchen, um mit den Folgen einer Schemaänderung umzugehen, und wenn die Arbeitselemente fehlen, sie nicht nur hinzufügen, sondern auch darüber informieren, wer sie aus dem Plan herausgelassen hat.
quelle
Fast immer erstelle ich beim Erstellen eines Exports auch einen entsprechenden Import. Da ich andere Tests habe, die die zu exportierende Datenstruktur vollständig ausfüllen, kann ich einen Roundtrip-Unit-Test erstellen, bei dem ein vollständig ausgefülltes Original mit einer exportierten und importierten Kopie verglichen wird. Wenn sie identisch sind, ist der Export / Import abgeschlossen. Wenn sie nicht identisch sind, schlägt der Komponententest fehl und ich weiß, dass der Exportmechanismus aktualisiert werden muss.
quelle