Erstellen von Komponententests auf einer CRUD-Ebene einer Anwendung. Wie kann ich die Tests unabhängig machen?

14

Ich versuche also, meine Unit-Tests so buchstäblich wie möglich zu gestalten, aber es wird problematisch, wenn ich einige einfache Methoden zum Hinzufügen / Löschen teste.

Für die Methode add muss ich grundsätzlich ein Dummy-Objekt erstellen und hinzufügen. Nach erfolgreichem Test muss ich das Dummy-Objekt löschen.

Und für den Löschtest muss ich natürlich ein Dummy-Objekt erstellen, damit ich es löschen kann.

Wie Sie sehen können, wenn ein Test fehlschlägt, schlägt auch der andere fehl, da beide irgendwie benötigt werden.

Dasselbe gilt für ein System, in dem Sie einen Test schreiben müssten, der "einen Auftrag storniert". Nun, es wäre ein Scheinauftrag erforderlich, um den Auftrag zuerst zu stornieren. Verstößt dies nicht gegen die Richtlinien des Komponententests?

Wie sollen solche Fälle behandelt werden?

Kyle
quelle
Vielleicht möchten Sie auch einen Blick auf diese Frage werfen: programmers.stackexchange.com/questions/115455/…
Guven

Antworten:

13

Nun, es ist nichts falsch an dem, was du tust. Mehrere Tests können denselben Code abdecken. Es bedeutet nur, dass ein Problem dazu führt, dass mehrere Tests fehlschlagen. Was Sie vermeiden möchten, sind Tests, die von den Ergebnissen anderer Tests abhängen. Dh, Ihr Löschtest hängt davon ab, ob der Hinzufügungstest ausgeführt wurde. Wenn Sie also Ihren Löschtest VOR dem Hinzufügungstest ausführen, schlägt der Test fehl. Um dieses Problem zu vermeiden, stellen Sie sicher, dass Sie zu Beginn jedes Tests eine "leere Tafel" haben, damit die nachfolgenden Tests nicht von den Vorgängen in einem bestimmten Test beeinflusst werden.

Eine gute Möglichkeit, dies zu tun, besteht darin, die Tests für eine In-Memory-Datenbank auszuführen.

Erstellen Sie in Ihrem Add-Test eine leere Datenbank, fügen Sie das Objekt hinzu und bestätigen Sie, dass es tatsächlich hinzugefügt wurde.

Erstellen Sie in Ihrem Löschtest die Datenbank mit dem Objekt, das Sie bereits löschen möchten. Löschen Sie das Objekt und bestätigen Sie, dass es gelöscht wurde.

Blasen Sie die Datenbank in Ihrem Abreißcode weg.

Adam Jaskiewicz
quelle
Die speicherinterne Datenbank ist nur schnell (im Speicher) und einfach (in Bearbeitung). Sie können dies mit jedem Datenspeicher tun.
Paul Draper
3

Verwenden Sie Transaktionen.

Wenn Sie eine Datenbank verwenden, die Transaktionen unterstützt, führen Sie jeden Test in einer Transaktion aus. Setzen Sie die Transaktion am Ende des Tests zurück. Dann lässt jeder Test die Datenbank unverändert.

Ja, um eine Bestellung zu stornieren, müssen Sie zuerst eine erstellen. Das ist gut. Der Test erstellt zuerst eine Bestellung, storniert sie dann und überprüft, ob die Bestellung storniert wurde.

Kevin Cline
quelle
Ich liebe diese Idee. Heute mit großer Wirkung umgesetzt.
Robopim
3

Du machst es gut. Das einzige grundlegende Prinzip beim Testen von Einheiten besteht darin, jeden Ihrer Codepfade abzudecken, sodass Sie sicher sein können, dass Ihr Code das tut, was er tun soll, und dies auch nach Änderungen und Umgestaltungen tut. Unit-Tests klein, einfach und zweckgebunden zu halten, ist ein lohnendes Ziel, aber nicht grundlegend. Ein Test, der zwei Relate-Methoden Ihrer API aufruft, ist an sich nicht fragwürdig. Wie Sie bereits betont haben, ist dies häufig erforderlich. Der Nachteil redundanter Tests besteht darin, dass das Schreiben länger dauert, aber wie bei fast allen in der Entwicklung befindlichen Tests ist dies ein Kompromiss, den Sie ständig eingehen müssen, und die beste Lösung ist so gut wie nie einer der Extrempunkte.

Kilian Foth
quelle
2

Die Techniken zum Testen von Software sind äußerst vielfältig. Je mehr Sie sich über sie informieren, desto mehr werden Sie unterschiedliche (und manchmal widersprüchliche) Anleitungen bemerken. Es gibt kein einziges 'Buch', an dem man vorbeikommt.

Ich denke, Sie befinden sich in einer Situation, in der Sie Anleitungen für Unit-Tests gesehen haben, die Dinge wie sagen

  • Jeder Test sollte eigenständig sein und nicht von anderen Tests beeinflusst werden
  • Jeder Komponententest sollte eine Sache und nur eine prüfen
  • Unit-Tests sollten die Datenbank nicht treffen

und so weiter. Und all das ist richtig, je nachdem, wie Sie "Unit Test" definieren .

Ich würde einen 'Komponententest' wie folgt definieren: "Ein Test, der eine Funktionseinheit für eine Codeeinheit ausführt, die von anderen abhängigen Komponenten isoliert ist."

Nach dieser Definition ist das, was Sie tun (wenn Sie einen Datensatz zu einer Datenbank hinzufügen müssen, bevor Sie den Test ausführen können), überhaupt kein "Komponententest", sondern eher ein "Integrationstest". (Ein echter Komponententest trifft meiner Definition nach nicht auf die Datenbank, sodass Sie keinen Datensatz hinzufügen müssen, bevor Sie ihn löschen.)

Bei einem Integrationstest werden Funktionen ausgeführt, die mehrere Komponenten verwenden (z. B. eine Benutzeroberfläche und eine Datenbank), und die für Komponententests geltenden Richtlinien gelten nicht unbedingt für Integrationstests.

Wie andere in ihren Antworten erwähnt haben, ist das, was Sie tun, nicht unbedingt falsch, auch wenn Sie Dinge tun, die gegen einige Richtlinien für Komponententests verstoßen. Versuchen Sie stattdessen zu überlegen, was Sie in den einzelnen Testmethoden wirklich testen. Wenn Sie feststellen, dass Sie mehrere Komponenten benötigen, um Ihren Test zu bestehen, und einige Komponenten vorkonfiguriert werden müssen, fahren Sie fort.

Vor allem aber sollten Sie sich darüber im Klaren sein, dass es viele Arten von Softwaretests gibt (Komponententests, Systemtests, Integrationstests, Erkundungstests usw.), und nicht versuchen, die Richtlinien eines Typs auf alle anderen anzuwenden.

Eric King
quelle
Wollen Sie damit sagen, dass Sie keine Unit-Tests zum Löschen aus der Datenbank durchführen können?
ChrisF
Wenn Sie auf die Datenbank zugreifen, handelt es sich (per Definition) um einen Integrationstest und nicht um einen Komponententest. Also in diesem Sinne nein. Sie können nicht "Unit-Test" aus einer Datenbank löschen. Sie können einen Komponententest durchführen, indem der zu testende Code zum Löschen einiger Daten aufgefordert wird und korrekt mit dem Datenzugriffsmodul interagiert.
Eric King
Aber der Punkt ist, dass manche Leute "Unit Test" anders definieren, also müssen wir vorsichtig sein, wenn wir "Unit Test" -Richtlinien anwenden, da die Richtlinien möglicherweise nicht so gelten, wie wir denken.
Eric King
1

Genau aus diesem Grund besteht eine der anderen Richtlinien darin, Schnittstellen zu verwenden. Wenn Ihre Methode ein Objekt verwendet, das eine Schnittstelle anstelle einer bestimmten Klassenimplementierung implementiert, können Sie eine Klasse erstellen, die nicht von der restlichen Codebasis abhängt.

Eine andere Alternative ist die Verwendung eines spöttischen Frameworks. Mit diesen können Sie auf einfache Weise solche Dummy-Objekte erstellen, die an die zu testende Methode übergeben werden können. Es ist möglich, dass Sie einige Stub-Implementierungen für die Dummy-Klasse erstellen müssen, diese jedoch immer noch eine Trennung von der tatsächlichen Implementierung und dem, worum es beim Test geht.

unholysampler
quelle
1

Wie Sie sehen können, wenn ein Test fehlschlägt, schlägt auch der andere fehl, da beide irgendwie benötigt werden.

So?

... verstößt dies nicht gegen die Richtlinien des Unit-Tests?

Nein.

Wie sollen solche Fälle behandelt werden?

Mehrere Tests können unabhängig voneinander ausgeführt werden, und alle schlagen aufgrund desselben Fehlers fehl. Das ist eigentlich normal. Viele Tests können - indirekt - einige gängige Funktionen testen. Und alle scheitern, wenn die gemeinsame Funktionalität nicht funktioniert. Daran ist nichts auszusetzen.

Komponententests sind als Klassen genau definiert , sodass sie Code problemlos gemeinsam nutzen können, wie ein allgemeiner Dummy-Datensatz, der zum Testen von Aktualisierungen und Löschungen verwendet wird.

S.Lott
quelle
1

Sie können ein Mockframework oder eine 'Umgebung' mit einer In-Memory-Datenbank verwenden. Die letzte Klasse ist eine Klasse, in der Sie alles erstellen können, was Sie zum Bestehen des Tests benötigen, bevor der Test ausgeführt wird.

Ich bevorzuge die letzte - die Benutzer können Ihnen bei der Eingabe einiger Daten helfen, damit Ihre Tests der realen Welt am nächsten kommen.

Andre
quelle
Richtig - aber Sie testen hier nicht die tatsächliche Datenbankverbindung. Es sei denn, Sie gehen davon aus, dass das immer funktionieren wird - aber Annahmen sind gefährlich.
ChrisF