Ist es eine schlechte Praxis, eine Ausführungsreihenfolge für Komponententests durchzusetzen?

84

Ich schreibe Tests für ein Projekt, das aus mehreren Submodulen besteht. Jeder Testfall, den ich geschrieben habe, läuft unabhängig voneinander und ich lösche alle Daten zwischen den Tests.

Obwohl die Tests unabhängig voneinander ausgeführt werden, erwäge ich, eine Ausführungsreihenfolge durchzusetzen, da in einigen Fällen mehr als ein Submodul erforderlich ist. Beispielsweise generiert ein Submodul Daten, und ein anderes Submodul führt Abfragen für die Daten aus. Wenn das Submodul, das die Daten generiert, einen Fehler enthält, schlägt auch der Test für das Abfragesubmodul fehl, selbst wenn das Submodul selbst einwandfrei funktioniert.

Ich kann nicht mit Dummy-Daten arbeiten, da die Hauptfunktion, die ich teste, die Verbindung zu einem Black-Box-Remote-Server ist, der nur die Daten vom ersten Submodul abruft.

Ist es in diesem Fall in Ordnung, eine Ausführungsreihenfolge für die Tests durchzusetzen, oder handelt es sich um eine falsche Vorgehensweise? Ich habe das Gefühl, dass es in diesem Setup einen Geruch gibt, aber ich kann keinen besseren Weg finden.

Bearbeiten: Die Frage lautet: Wie werden Tests strukturiert, bei denen ein Test der Aufbau eines anderen Tests ist? da der "vorherige" Test kein Setup ist, sondern den Code testet, der das Setup durchführt.

Ali Rasim Kocal
quelle
123
Wenn Sie die Verbindung zu einem Remote-Server testen, handelt es sich definitionsgemäß nicht um Komponententests.
Telastyn
9
Die erste Antwort hat mich hier verwirrt, weil Sie in Ihrem Titel gesagt haben: "Ist es schlechte Praxis?" und in der Zusammenfassung Ihrer Frage haben Sie geschrieben: "Ist es in Ordnung?" Jeder, der mit Ja oder Nein antwortet, wird einen davon verwirren!
Liath
8
Klingt so, als würden Sie eine Reihe von Integrationstests erstellen. Auch für diesen einen Test sollte man sich nicht auf andere Tests verlassen.
Tieffliegender Pelikan
17
Wenn die Reihenfolge wichtig ist, machen Sie es wahrscheinlich falsch.
Mark Rogers

Antworten:

236

Ich kann nicht mit Dummy-Daten arbeiten, da die Hauptfunktion, die ich teste, die Verbindung zu einem Black-Box-Remote-Server ist, der nur die Daten vom ersten Submodul abruft.

Dies ist der Schlüssel für mich. Sie können von "Komponententests" und "unabhängig voneinander laufen" sprechen, aber sie klingen alle so, als wären sie auf diesen Remote-Server und auf das "erste Submodul" angewiesen. Es klingt also alles eng gekoppelt und abhängig vom äußeren Zustand. Als solches schreiben Sie tatsächlich Integrationstests. Es ist ganz normal, dass diese Tests in einer bestimmten Reihenfolge ausgeführt werden, da sie stark von externen Faktoren abhängen. Ein bestellter Testlauf mit der Möglichkeit, den Testlauf vorzeitig abzubrechen, falls etwas schief geht, ist für Integrationstests durchaus akzeptabel.

Es lohnt sich aber auch, einen Blick auf die Struktur Ihrer App zu werfen. Wenn Sie in der Lage sind, das erste Submodul und den externen Server zu verspotten, können Sie möglicherweise echte Komponententests für alle anderen Submodule schreiben.

David Arno
quelle
14
Ganz zu schweigen davon, dass einige Tests speziell prüfen müssen, ob das erwartete Verhalten auftritt, wenn der Remoteserver nicht verfügbar ist.
Alexander
2
Oder Sie beabsichtigen tatsächlich, Integrationstests zu schreiben, und daher wird das Verspotten der Daten nicht das erreichen, was Sie mit diesen Tests erreichen möchten.
Guy Schalnat
10
Das Problem ist höchstwahrscheinlich, dass Junit "unit" im Namen hat.
Thorbjørn Ravn Andersen
7
@ ThorbjørnRavnAndersen Genau. Die Leute schreiben natürlich eher Integrationstests als Unit-Tests, weil Integrationstests sowohl weitaus nützlicher als auch weitaus weniger schwierig zu schreiben sind als "echte" Unit-Tests. Da die gängigen Test-Frameworks jedoch nach dem Konzept der Komponententests benannt wurden, wurde der Begriff übernommen und bedeutet im modernen Sprachgebrauch "automatisiertes Testen".
Mason Wheeler
1
@MasonWheeler Oder sogar von nicht-technischen Managern als Akzeptanztest bezeichnet.
TKK
32

Ja, es ist eine schlechte Praxis.

Im Allgemeinen soll ein Komponententest eine einzelne Codeeinheit testen (z. B. eine einzelne Funktion basierend auf einem bekannten Zustand).

Wenn Sie eine Kette von Ereignissen testen möchten, die in der Natur auftreten können, möchten Sie einen anderen Teststil, z. B. einen Integrationstest. Dies gilt umso mehr, wenn Sie von einem Drittanbieter abhängig sind.

Um solche Dinge einem Komponententest zu unterziehen, müssen Sie eine Möglichkeit zum Einfügen der Dummy-Daten finden, beispielsweise eine Datendienstschnittstelle implementieren, die die Webanforderung spiegelt, jedoch bekannte Daten aus einer lokalen Dummy-Datendatei zurückgibt.

Paul
quelle
8
Einverstanden. Ich denke, diese Verwirrung rührt von der Tatsache her, dass viele Menschen die Idee haben, dass Integrationstests durchgängig sein müssen, und verwenden "Komponententest", um sich auf jeden Test zu beziehen, der nur eine Ebene testet .
Autophage
8
@autophage: Ich stimme dem definitiv zu. Tatsächlich stimme ich dem so sehr zu, dass ich regelmäßig in dieselbe Falle tappe, obwohl ich der Meinung bin, dass es sich um eine Falle handelt 😂
Leichtigkeitsrennen im Orbit
16

Die von Ihnen vorgeschlagene erzwungene Ausführungsreihenfolge ist nur dann sinnvoll, wenn Sie den Testlauf auch nach dem ersten Fehler abbrechen.

Der Abbruch des Testlaufs bei dem ersten Fehler bedeutet, dass jeder Testlauf nur ein einziges Problem aufdecken kann und keine neuen Probleme finden kann, bis alle vorhergehenden Probleme behoben wurden. Wenn der erste ausgeführte Test ein Problem feststellt, dessen Behebung einen Monat in Anspruch nimmt, werden in diesem Monat effektiv keine Tests ausgeführt.

Wenn Sie den Testlauf bei dem ersten Fehler nicht abbrechen, bringt Ihnen die erzwungene Ausführungsreihenfolge nichts, da jeder fehlgeschlagene Test ohnehin untersucht werden muss. Auch wenn nur zur Bestätigung, dass der Test auf dem Abfragesubmodul aufgrund des Fehlers, der auch auf dem datenerzeugenden Submodul festgestellt wurde, fehlschlägt.

Der beste Rat, den ich geben kann, besteht darin, die Tests so zu schreiben, dass leicht festgestellt werden kann, wann ein Fehler in einer Abhängigkeit dazu führt, dass der Test fehlschlägt.

Bart van Ingen Schenau
quelle
7

Der Geruch, auf den Sie sich beziehen, ist die Anwendung der falschen Einschränkungen und Regeln auf Ihre Tests.

Unit Tests werden oft mit "automatisierten Tests" oder "automatisierten Tests durch Programmierer" verwechselt.

Unit Tests müssen klein, unabhängig und schnell sein.

Einige Leute lesen dies fälschlicherweise als "automatisierte Tests, die von einem Programmierer geschrieben wurden, müssen klein, unabhängig und schnell sein" . Wenn Ihre Tests jedoch nicht klein, unabhängig und schnell sind, handelt es sich nicht um Komponententests. Daher sollten, können oder müssen einige der Regeln für Komponententests nicht für Ihre Tests gelten. Ein einfaches Beispiel: Sie sollten Ihre Komponententests nach jedem Build ausführen, was bei automatisierten Tests, die nicht schnell sind, nicht erforderlich ist.

Während Ihre Tests keine Komponententests sind, bedeutet dies, dass Sie eine Regel überspringen können und eine gewisse Abhängigkeit zwischen den Tests haben dürfen. Sie haben jedoch auch herausgefunden, dass es andere Regeln gibt, die Sie möglicherweise übersehen haben und erneut einführen müssen - etwas für den Umfang einer anderen Frage .

Peter
quelle
6

Wie oben erwähnt, scheint das, was Sie ausführen, ein Integrationstest zu sein, Sie geben jedoch Folgendes an:

Beispielsweise generiert ein Submodul Daten, und ein anderes Submodul führt Abfragen für die Daten aus. Wenn das Submodul, das die Daten generiert, einen Fehler enthält, schlägt auch der Test für das Abfragesubmodul fehl, selbst wenn das Submodul selbst einwandfrei funktioniert.

Und dies könnte ein guter Ort sein, um mit dem Refactoring zu beginnen. Das Modul, das Abfragen zu den Daten ausführt, sollte nicht von einer konkreten Implementierung des ersten (datengenerierenden) Moduls abhängig sein. Stattdessen ist es besser, eine Schnittstelle einzufügen, die die Methoden enthält, um an diese Daten zu gelangen, und diese kann dann zum Testen der Abfragen ausgeblendet werden.

z.B

Wenn Sie haben:

class Queries {

    int GetTheNumber() {
        var dataModule = new Submodule1();
        var data = dataModule.GetData();
        return ... run some query on data
    }
}

Stattdessen bevorzugen:

interface DataModule {
    Data GetData();
}


class Queries {

    IDataModule _dataModule;

    ctor(IDataModule dataModule) {
       _dataModule = dataModule;
    }

    int GetTheNumber() {
        var data = _dataModule.GetData();
        return ... run some query on data
    }
}

Dadurch wird die Abhängigkeit von Abfragen zu Ihrer Datenquelle aufgehoben, und Sie können leicht wiederholbare Komponententests für bestimmte Szenarien einrichten.

Paddy
quelle
6

Die anderen Antworten erwähnen, dass die Testreihenfolge schlecht ist (was die meiste Zeit der Fall ist), aber es gibt einen guten Grund, die Reihenfolge bei der Testausführung durchzusetzen: Stellen Sie sicher, dass Ihre langsamen Tests (dh Integrationstests) nach Ihren schnelleren Tests (Tests) ausgeführt werden die nicht auf andere externe Ressourcen angewiesen sind). Dies stellt sicher, dass Sie mehr Tests schneller ausführen, was die Rückkopplungsschleife beschleunigen kann.

Mike Holler
quelle
2
Ich würde eher untersuchen, warum ein bestimmter Komponententest langsam abläuft, als einen Befehl durchzusetzen. Unit-Tests sollen schnell sein.
Robbie Dee
Das OP sagt, dass er für einige dieser Tests nicht mit Dummy-Daten arbeiten kann. Das bedeutet, dass eine Art Datenbank-Hit alle Tests verlangsamt (sogar einige echte Unit-Tests, die natürlich schnell ablaufen sollten). Wenn er über andere Tests verfügt, für die keine Datenbanktreffer erforderlich sind, werden diese um eine Größenordnung schneller ausgeführt als alle Tests, für die Datenträger- oder Netzwerktreffer erforderlich sind.
Mike Holler
2
Sie haben beide recht, denke ich. Robbie hat Recht, dass Komponententests klein und schnell und unabhängig von Abhängigkeiten sein sollten, damit die Reihenfolge keine Rolle spielt. Die zufällige Reihenfolge fördert häufig ein besseres Design, indem sie diese Unabhängigkeit erzwingt. Und Mike hat Recht, dass es für Integrationstests sehr, sehr gut ist, zuerst schnellere Tests durchzuführen . Wie in den obigen Antworten ist ein Teil des Problems die Terminologie von Unit- vs. Integrationstests.
WillC
@MikeHoller Dann sind sie keine Unit-Tests. Es sollte wirklich keine Verwirrung darüber geben, was Unit-Tests sind .
Robbie Dee
@RobbieDee Ich habe lediglich die Terminologie verwendet, die das OP verwendet hat. Ich verstehe, dass dies keine echten Unit-Tests sind. Wenn Sie sich mit Terminologie auseinandersetzen möchten, wenden Sie sich an OP. (daher habe ich in meinem früheren Kommentar mit "True Unit Tests" geklärt)
Mike Holler