Warum In-Memory-Datenbank für Integrationstests verwenden?

18

Ich bin wirklich verwirrt, wenn ich sehe, dass viele In-Memory-Datenbankimplementierungen zum Testen verwendet werden, da ich auch von den Best Practices für Integrationstests gehört habe, dass die Umgebung, in der der Test ausgeführt wird, der Produktionsumgebung, einschließlich des Betriebssystems, so ähnlich wie möglich sein sollte , Bibliothek, Datenbankmodul usw.

Was vermisse ich hier?

dnang
quelle
Ich habe es oft gesehen, weil Sie programmgesteuert die Datenkonsistenz sicherstellen können und sie im Allgemeinen relativ schnell sind. Insbesondere wenn es sich bei Ihrem Test um einen Komponententest handelt, möchten Sie, dass es sich um ein geschlossenes System handelt. Sie möchten, dass Ihre Tests vollständig in den Test einbezogen werden.
Rig
Es ist schön, wenn ein Entwickler in der Lage ist, den neuesten Code abzurufen und die Tests auszuführen, ohne zuvor eine externe Datenbank einrichten zu müssen.
Jason Evans
1
Die Aspekte, die eine starke Ähnlichkeit mit der Produktionsumgebung erfordern, sind Lasttests und Stresstests, die meiner Meinung nach getrennt von Unit-Tests (die als Code-Commit-Gate-Kriterien gelten) durchgeführt werden sollten.
Rwong

Antworten:

19

In einer typischen Softwareentwicklungssituation werden Tests an zwei Punkten verwendet: während der Entwicklung und bevor das Produkt entlang der Entwicklungskette verschoben wird.

Die erste Situation, in der Tests während der Entwicklung ausgeführt werden, dient kurzfristigen Zielen: Definieren von Aufgaben (wie in TDD: Schreiben eines fehlerhaften Tests, Bestehen des Tests), Verhindern von Regressionen, Sicherstellen, dass Ihre Änderungen nichts anderes beschädigen usw. Wie z Tests müssen extrem schnell sein: Im Idealfall dauert Ihre gesamte Testsuite weniger als 5 Sekunden, und Sie können sie einfach in einer Schleife neben Ihrer IDE oder Ihrem Texteditor ausführen, während Sie programmieren. Jede von Ihnen eingeführte Regression wird innerhalb von Sekunden angezeigt. Schnelle Testläufe sind in dieser Phase wichtiger als das Erkennen von 100% aller Regressionen und Fehler. Da es unpraktisch (oder unmöglich) ist, exakte Kopien der Produktionssysteme zu entwickeln, lohnt sich der Aufwand, um hier perfekte Tests zu erzielen, nicht es. Die Verwendung von In-Memory-Datenbanken ist ein Kompromiss: Sie sind keine exakten Kopien des Produktionssystems. Sie tragen jedoch dazu bei, die Testläufe unter der 5-Sekunden-Grenze zu halten. Wenn die Wahl zwischen einem etwas anderen Datenbank-Setup für meine datenbankbezogenen Tests und überhaupt keinem Test liegt, weiß ich, was ich auswähle.

Die zweite Situation, den Code entlang der Entwicklungskette bewegen, jedoch hat umfangreiche Tests erforderlich. Da wir diesen Teil des Entwicklungsprozesses automatisieren können (und sollten), können wir uns viel langsamere Tests leisten - selbst wenn ein vollständiger Testlauf Stunden dauert, bedeutet die Planung eines nächtlichen Builds, dass wir immer ein genaues Bild der gestrigen Codebasis haben. Es ist jetzt wichtig, die Produktionsumgebung so genau wie möglich zu simulieren, aber wir können es uns leisten. Wir machen also keinen Kompromiss zwischen In-Memory-Datenbank: Wir installieren die exakt gleiche Version des exakt gleichen DBMS wie die Produktionssysteme und füllen es, wenn möglich, mit tatsächlichen Produktionsdaten, bevor der Test beginnt.

tdammers
quelle
6

Ich denke, es ist ein Kompromiss zwischen Geschwindigkeit und Umgebung. Tests sollten oft durchgeführt werden und das bedeutet, dass sie schnell sein müssen. Besonders Unit-Tests, die nicht länger als ein paar Sekunden dauern sollten.

Integrationstests werden langsamer ausgeführt, aber wenn sie schnell sind, können Sie sie häufiger ausführen. Zum Beispiel vor jedem Commit. Natürlich ist es nicht so vollständig wie eine vollständige Umgebung, aber zumindest testen Sie die Mapping-Ebene, das generierte SQL, wie die Teile miteinander kommunizieren usw. Im Falle einer teuren Datenbank stellen Sie auch sicher, dass Sie keine Änderungen vornehmen. Sie müssen keine Lizenz für alle kaufen. Bei Tests, die einmal pro Stunde ausgeführt werden, können mehr Fehler auftreten, die 90% des Codes abdecken, als bei 100% des Codes, der einmal pro Tag oder in der schlechtesten Woche ausgeführt wird.

Davon abgesehen benötigen Sie natürlich einen Test mit der realen Datenbank und einer vollständig integrierten Umgebung. Sie können diese Tests nicht so oft ausführen, aber da Ihr vorheriger Test Ihnen bereits Vertrauen verlieh, ist alles, was übrig bleibt, ein seltsamer plattformspezifischer Fehler.

Laurent Bourgault-Roy
quelle
3

Für einfache Tests ist es durchaus akzeptabel, die Datenbankzugriffsebene zu verspotten. Sie rufen an getName(), es ruft das verspottete DAO auf und gibt "John" für den Vornamen und "Smith" für den Nachnamen zurück, setzt sie zusammen und alles ist perfekt. Es ist nicht erforderlich, eine Datenbank dort tatsächlich als Einheit zu testen.

Die Dinge werden ein bisschen komplexer, wenn die Logik ein bisschen komplexer wird. Was wäre, wenn Sie eine Methode "createOrUpdateUser (...)" hätten? Wenn Sie die Datenbank verspottet haben, können Sie überprüfen, ob eine bestimmte Methode einmal mit einem bestimmten Parameter aufgerufen wurde, wenn die Verspottung keine Objekte zurückgibt, und eine andere Methode für die Datenbank aufgerufen wird, wenn sie ein vorhandenes Objekt zurückgibt. Dies führt zu der unscharfen Zeile, in der es möglicherweise einfacher ist (insbesondere, wenn sie bereits vorhanden ist), eine spezialisierte Speicherdatenbank hochzufahren und diesen Code mit vorkonfigurierten Daten zu testen.

In einem Code, an dem ich gearbeitet habe (Verkaufsstelle), hatten wir eine resumeSuspededTransaction(...)Methode. Dies würde die Transaktion aus der Datenbank in ein Objekt (und seine Komponenten) ziehen und die Datenbank aktualisieren. Wir hatten es verspottet und irgendwo im Code lauerte ein Fehler mit der Serialisierung und Deserialisierung der Daten, die in die Datenbank gingen (wir haben einen Typ geändert, der in der Datenbank anders serialisiert wurde).

Der Mock hat uns den Fehler nicht gezeigt, weil er seinen fehlerfreien Pfad zurückgegeben hat - serialisieren Sie die Transaktion, speichern Sie sie im Mock, deserialisieren Sie sie vom Mock und testen Sie, ob sie gleich sind. Wenn Sie jedoch ein Objekt mit einer führenden Null in die Datenbank serialisieren, löschte es diese und kombinierte sie dann wieder zu einer Zeichenfolge ohne Nullen. Wir haben den Fehler ohne die Datenbank durch Fehlerbehebung entdeckt (es war nicht so schwer zu erkennen, sobald wir wussten, dass es da ist).

Später haben wir dort eine Datenbank abgelegt und festgestellt, dass der Fehler diesen Junit-Test nie bestanden hätte, wenn wir stattdessen eine In-Memory-Datenbank verwendet hätten.


In Speicherdatenbanken haben die Vorteile:

  • Sie können schnell gestartet werden (ohne dass ein Datenbankadministrator zum Einrichten von Konten, Tabellen usw. erforderlich ist)
  • Die Daten können für diesen Test vorkonfiguriert werden
  • Der Test muss sich nicht darum kümmern, den Test zurückzusetzen, wenn er abgeschlossen ist
  • Jeder Test verfügt über eine eigene Speicherdatenbank, sodass Sie sich keine Sorgen machen müssen, wenn zwei Tests gleichzeitig ausgeführt werden
  • Sie können auf Systemen ausgeführt werden, die keine Verbindung zu den realen Datenbanken haben

quelle
1

Dies hängt stark vom verwendeten Datenbanksystem ab. Wenn Ihr Datenbanksystem eine In-Memory-Alternative bietet, die zu fast 100% mit der API und dem Verhalten einer festplattenbasierten Datenbankkonfiguration kompatibel ist (mit Ausnahme von Geschwindigkeit und Ausfallsicherheit oder natürlich), ist die Verwendung der In-Memory-Variante offensichtlich in Ordnung .

Wenn Ihr DB-System jedoch erhebliche Unterschiede zwischen der speicherinternen Konfiguration und der nicht speicherinternen Nutzung aufweist, haben Sie Recht: In diesem Fall besteht bei Integrationstests ein höheres Risiko, dass ein Fehler auftritt. Aber selbst dann können Sie diese Unterschiede möglicherweise selbst "abstrahieren", vorausgesetzt, Sie kennen Ihr DB-System und die Unterschiede gut.

Doc Brown
quelle
1

Mit den Worten des Laien:

Die Verspottung wichtigen Teile der Architektur in Ordnung ist (und ein Muss) für Unit - Tests .

Aber für Integrationstests stimme ich Ihnen voll und ganz zu. Es sollte nicht verspottet werden und eine möglichst ähnliche Umgebung wie die reale zur Verfügung gestellt werden.

Schließlich geht es bei Integrationstests darum, zu testen, wie sich die verschiedenen Teile der Architektur zusammen verhalten.

Tulains Córdova
quelle