Verstößt das Testen einzelner Einheiten nicht gegen das DRY-Prinzip?


12

Wenn ich Unit-Tests schreibe, habe ich immer versucht, eine einzige Bestätigung pro Test zu erstellen, um das Debuggen zu vereinfachen, wenn Tests fehlschlagen. Wenn ich mich jedoch an diese Regel halte, habe ich das Gefühl, dass ich in jedem Test ständig denselben Code kopiere. Wenn ich mehr Tests habe, wird es schwieriger, zum Lesen und Verwalten zurückzukehren.

Verstößt das Testen mit einer Behauptung gegen DRY?

Und gibt es eine gute Regel, um ein gutes Gleichgewicht zu finden, beispielsweise nur einen Test pro Methode ? *

* Mir ist klar, dass es wahrscheinlich keine einheitliche Lösung dafür gibt, aber gibt es einen empfohlenen Weg, dies zu erreichen?


5
Sie könnten den Code, den Sie kopieren, in Methoden extrahieren
Ismail Badawi

@IsmailBadawi das klingt nach einer guten Idee. Ich würde annehmen, dass diese Methoden eine Instanz des Klassenobjekts zurückgeben sollten, das ich teste
Korey Hinton

1
Sie erstellen etwas in einem bestimmten Zustand, das getestet werden soll? Das klingt nach einem Fixpunkt.
Dave Hillier

@ DaveHillier ja, ich habe heute ein neues Wort gelernt. Danke :)
Korey Hinton

hängt davon ab, wie Sie 1 Assert pro Test interpretieren. Wenn Sie einen Assert * -Aufruf meinen, dann ja, wenn Sie auch sicherstellen möchten, dass die Invarianten weiterhin gültig sind (und diese dann erneut in eine Methode extrahieren), oder wenn es mehrere Effekte gibt, die Sie einfach können. ' t Test in einem einzigen Assert (oder wenn Sie dies taten, wäre es nicht klar, warum es fehlgeschlagen ist)
Ratschenfreak

Antworten:


14

Richtige Komponententests haben eine Namenskonvention , mit der Sie sofort feststellen können, was fehlgeschlagen ist:

public void AddNewCustomer_CustomerExists_ThrowsException()

Aus diesem Grund haben Sie eine Zusicherung pro Test, sodass jede Methode (und ihr Name) der Bedingung entspricht, die Sie geltend machen.

Wie Sie richtig betont haben, wird jeder neue Test einen ähnlichen Setup-Code haben. Wie bei jedem Code können Sie den allgemeinen Code in eine eigene Methode umgestalten, um die Duplizierung zu reduzieren oder zu beseitigen und Ihren Code trockener zu machen. Einige Test-Frameworks wurden speziell entwickelt, damit Sie diesen Setup-Code an einem Ort ablegen können .

In TDD ist kein Test YAGNI, da Sie Tests nur basierend auf den Anforderungen Ihres Codes schreiben. Wenn Sie es nicht brauchen, werden Sie den Test nicht schreiben.


Refactoring in eine einzelne Methode klingt gut. Wenn ich eine Instanz der Klasse in einem bestimmten Zustand benötigen würde, könnte ich eine neue Instanz dieses Objekts in der überarbeiteten Methode erstellen und zurückgeben oder wie Sie vorschlagen, die vom Testframework bereitgestellten Methoden für die anfängliche Testeinrichtung verwenden.
Korey Hinton

auf dem letzten Punkt, ich bin sicher , dass ich Tests für Funktionalität schreiben konnte ich würde gerne den Code haben, anstatt Funktionalität es benötigt
Joel B

5

Verstößt das Testen mit einer Behauptung gegen DRY?

Nein, aber es fördert Verstöße.

Trotzdem tendiert gutes objektorientiertes Design dazu, für Unit-Tests aus dem Fenster zu gehen - meistens aus gutem Grund. Es ist wichtiger, dass Komponententests voneinander isoliert sind, damit der Test isoliert abgefragt und bei Bedarf mit der Gewissheit behoben werden kann, dass Sie andere Tests nicht brechen. Grundsätzlich ist diese Testkorrektheit und Lesbarkeit wichtiger als ihre Größe oder Wartbarkeit.

Ehrlich gesagt war ich aus den von Ihnen beschriebenen Gründen noch nie ein Fan der einen Assert-per-Test-Regel: Sie führt zu einer Menge Code auf dem Boilerplate, der schwer zu lesen, leicht zu verfälschen und schwer zu reparieren ist, wenn Sie den Refactor durchführen (was Sie dazu bringt, weniger umzugestalten).

Wenn eine Funktion eine Liste von "foo" und "bar" für eine bestimmte Eingabe zurückgeben soll, ist es jedoch völlig in Ordnung, zwei Asserts zu verwenden, um zu überprüfen, ob beide in der Ergebnismenge enthalten sind. Sie geraten in Schwierigkeiten, wenn ein einzelner Test zwei Eingänge oder zwei Nebenwirkungen überprüft und Sie nicht wissen, welcher der beiden den Fehler verursacht hat.

Ich betrachte es als eine Variation des Einzelverantwortungsprinzips: Es sollte nur eines geben, das dazu führen kann, dass ein Test fehlschlägt, und in einer idealen Welt sollte diese Änderung nur einen Test brechen.

Aber am Ende ist es ein Kompromiss. Ist es wahrscheinlicher, dass Sie mehr Zeit damit verbringen, den gesamten Code zum Kopieren und Einfügen zu verwalten, oder werden Sie mehr Zeit damit verbringen, nach Ursachen zu suchen, wenn Tests durch mehrere Quellen unterbrochen werden könnten? Solange Sie einige Tests schreiben, spielt es wahrscheinlich keine Rolle. Trotz meiner Verachtung für Single-Assert-Tests neige ich dazu, mich auf die Seite weiterer Tests zu stellen. Ihr Kilometerstand kann variieren.


1
Für Tests ist es wichtiger, DAMP als DRY (Descriptive And Meaningful Phrases) zu sein.
Jörg W Mittag

2

Nein. Dies scheint genau so zu sein, wie Sie es tun. Es sei denn, Sie haben eine bemerkenswerte Referenz gefunden, in der behauptet wird, dies sei eine gute Praxis.

Verwenden Sie ein Testgerät (obwohl in der XUnit-Terminologie der Satz von Tests, das Einrichten und Herunterfahren das Gerät ist), dh einige Einstellungen oder Beispiele, die für alle Ihre Tests gelten.

Verwenden Sie Methoden, wie Sie es normalerweise tun würden, um Ihren Code zu strukturieren. Bei Refactoring-Tests gilt nicht der übliche TDD- Rot-Grün-Refactor , sondern "Refactoring in the Red". Das ist,

  1. brechen Sie absichtlich Ihren Test,
  2. Mach dein Refactoring
  3. Repariere deinen Test

Auf diese Weise wissen Sie, dass die Tests immer noch positive und negative Ergebnisse liefern.

Es gibt verschiedene Standardformate für Tests. Beispiel: Anordnen, Handeln, Bestätigen oder Gegeben wann, dann (BDD) . Verwenden Sie für jeden Schritt eine separate Funktion. Sie sollten in der Lage sein, die Funktion zum Reduzieren der Kesselplatte aufzurufen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.