it-swarm.com.de

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

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 führt Abfragen zu den Daten aus. Wenn das die Daten generierende Submodul einen Fehler enthält, schlägt der Test für das Abfragesubmodul ebenfalls fehl, selbst wenn das Submodul selbst einwandfrei funktioniert.

Ich kann nicht mit Dummy-Daten arbeiten, da die Hauptfunktionalität, die ich teste, die Verbindung zu einem Black-Box-Remote-Server ist, der nur die Daten vom ersten Submodul erhält.

Ist es in diesem Fall in Ordnung, eine Ausführungsreihenfolge für die Tests durchzusetzen, oder ist es eine schlechte Praxis? Ich habe das Gefühl, dass dieses Setup riecht, aber ich kann keinen besseren Weg finden.

bearbeiten: Die Frage stammt von Wie werden Tests strukturiert, bei denen ein Test das Setup eines anderen Tests ist? da der "vorherige" Test kein Setup ist, sondern den Code testet, der das Setup ausführt.

84
Ali Rasim Kocal

Ich kann nicht mit Dummy-Daten arbeiten, da die Hauptfunktionalität, die ich teste, die Verbindung zu einem Black-Box-Remote-Server ist, der nur die Daten vom ersten Submodul erhält.

Dies ist der Schlüsselteil für mich. Sie können von "Unit-Tests" und "unabhängig voneinander laufenden" Tests sprechen, aber alle klingen so, als wären sie von diesem Remote-Server und vom "ersten Submodul" abhängig. Alles klingt also 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 geordneter Testlauf mit der Option, den Testlauf vorzeitig zu beenden, wenn etwas schief geht, ist für Integrationstests durchaus akzeptabel.

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

236
David Arno

Ja, das ist eine schlechte Praxis.

Im Allgemeinen soll ein Einheitentest 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 freier Wildbahn auftreten können, möchten Sie einen anderen Teststil, z. B. einen Integrationstest. Dies gilt umso mehr, wenn Sie auf einen Drittanbieter angewiesen sind.

Um solche Tests zu testen, müssen Sie einen Weg finden, um die Dummy-Daten zu injizieren, z. B. eine Datendienstschnittstelle implementieren, die die Webanforderung spiegelt, aber bekannte Daten aus einer lokalen Dummy-Datendatei zurückgibt.

33
Paul

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

Das Abbrechen des Testlaufs beim ersten Fehler bedeutet, dass jeder Testlauf nur ein einziges Problem aufdecken und keine neuen Probleme finden kann, bis alle vorhergehenden Probleme behoben wurden. Wenn der erste Test, der ausgeführt wird, ein Problem findet, dessen Behebung einen Monat dauert, werden in diesem Monat effektiv keine Tests ausgeführt.

Wenn Sie den Testlauf beim ersten Fehler nicht abbrechen, kauft Ihnen die erzwungene Ausführungsreihenfolge nichts, da jeder fehlgeschlagene Test ohnehin untersucht werden muss. Auch wenn nur bestätigt werden soll, dass der Test für das Abfragesubmodul aufgrund des Fehlers fehlschlägt, der auch für das Datengenerierungs-Submodul festgestellt wurde.

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

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 einen Programmierer" verwechselt.

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

Einige Leute lesen dies fälschlicherweise als "von einem Programmierer geschriebene automatisierte Tests müssen klein, unabhängig und schnell sein". Aber es bedeutet einfach, wenn Ihre Tests nicht klein, unabhängig und schnell sind, sind sie keine Komponententests, und daher sollten einige der Regeln für Komponententests nicht gelten, können nicht oder darf nicht gelten Ihre Tests. Ein triviales Beispiel: Sie sollten Ihre Unit-Tests nach jedem Build ausführen, was Sie für automatisierte Tests, die nicht schnell sind, nicht tun dürfen.

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

7
Peter

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 führt Abfragen zu den Daten aus. Wenn das die Daten generierende Submodul einen Fehler enthält, schlägt der Test für das Abfragesubmodul ebenfalls fehl, selbst wenn das Submodul selbst einwandfrei funktioniert.

Und dies kann 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 auf diese Daten zuzugreifen, und diese kann dann zum Testen der Abfragen verspottet 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 von Ihrer Datenquelle beseitigt und Sie können leicht wiederholbare Komponententests für bestimmte Szenarien einrichten.

6
Paddy

In den anderen Antworten wird erwähnt, dass das Bestellen von Tests schlecht ist (was meistens 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.

6
Mike Holler