Nicht-transaktionale Datenbank- und Integrationstests

8

Ein Problem, auf das ich bei meinen Integrationstests stoßen werde, ist, dass mehrere Tests auf dieselbe Datenbank zugreifen. Obwohl dies jetzt kein Problem ist, weiß ich, dass wir hier mehrere Anwendungen haben, die auf dieselbe Datenbank zugreifen, und ich versuche nur, einen Weg zu finden, um dieses Problem zu verhindern, bevor es auftritt.

Eine Idee, die ich oft gesehen habe, ist die Verwendung von Transaktionen. Beim Start starten Sie eine Transaktion und beim Herunterfahren setzen Sie die Transaktion zurück. Dies bedeutet, dass mehrere Tests auf dieselben Datenbanktabellen zugreifen und sich nicht gegenseitig beeinflussen, was großartig ist. Das Problem, das ich habe, ist, dass in meinem Fall 85-95% der Tabellen, mit denen ich in MySQL arbeite, MyISAM sind, die keine Transaktionen unterstützen.

Gibt es Möglichkeiten, um Speicher-Engines zu umgehen, die keine Transaktionen unterstützen, aber dennoch mehreren Tests den Zugriff auf dieselben Tabellen ermöglichen, ohne dass sie sich gegenseitig beeinflussen? Soweit ich gehört habe, verwendet das Ruby on Rails-Testframework Transaktionen auf diese Weise. Wie umgehen sie dieses Problem (oder tun sie es)?

Ryanzec
quelle
Führen Sie Tests gleichzeitig durch?
JeffO
Ich könnte. Ich würde nicht gleichzeitig Tests desselben Projekts durchführen, aber wir haben mehrere Projekte, die auf dieselbe Datenbank / Tabellen zugreifen. Ich konnte 2 Projekte sehen, die gleichzeitig ihre Tests ausführten.
Ryanzec
1
+1 Es ist noch schwieriger, wenn Sie Tests gleichzeitig ausführen müssen.
KLE

Antworten:

4

In meinem Unternehmen gab es diese Debatte: Persistenz ist ein großes Problem beim Testen.

Es gibt einige Hacks, die Sie auf halbem Weg bringen. Wir haben beschlossen, unsere Zeit mit ihnen nicht zu verlieren:

  • Verwenden von Transaktionen, die Sie zurücksetzen . Das scheitert als:
    • Manchmal öffnen Sie eine isolierte Transaktion (innerhalb einer allgemeineren), sodass sie geschlossen wurde, bevor Ihr Testcode sie in den Griff bekommen kann
    • Sie können Ihre wahre Ausdauer nicht testen. (Was passiert, wenn ein Test eine Transaktion schließt und eine komplexe Abfrage ausführt, die von diesen Daten in einer anderen Transaktion betroffen ist, oder Daten speichert und später erneut lädt?)
    • Sie können Ihren Code nicht vollständig testen (nur die Ebenen unterhalb der Transaktionsebene; nicht gut genug, wenn Sie die Nachteile von Integrationstests berücksichtigen).
  • Verwenden einer frischen, sauberen Datenbank für jeden Test . Das scheitert auch als:
    • Unsere Testsuite wird bald einige tausend Tests bestehen (wir haben große Teams). Das Einrichten einer neuen Datenbank erfordert bestenfalls Minuten (da wir viele Kataloge und Parameter haben, die Teil jeder Version sind). Die Fertigstellung unserer Testsuite würde bald Monate dauern, und wir können nur einige Tage warten.
    • Wir benötigen eine bestimmte Datenbankinstanz für jeden Entwickler oder jeden Computer und haben eine Richtlinie, um jeweils nur einen Test zu starten. Wir müssen Oracle 11 verwenden, um zu überprüfen, ob unsere Abfragen in Ordnung sind. Dies wäre zu kostspielig (Oracle-Lizenzen).
  • Sorgfältige Bereinigung nach jedem Test (Test-Frameworks bieten Hooks zum Ausführen von Code nach einem Test). Das scheitert als:
    • Es ist sehr kostspielig, eine perfekte Übereinstimmung zwischen dem Code doesund dem Test aufrechtzuerhalten undoes. Sobald die Übereinstimmung nicht perfekt ist, die Symptome nicht klar sind, kann ein Test einige hundert Zeilen später fehlschlagen. oder ein Test, der fehlschlagen sollte, könnte fehlerhaft bestehen.
    • Es gibt immer Randfälle, in denen die Reinigung nicht korrekt ist (Maschinen- oder Software-Absturz?). Damit bleibt ein unbekannter Zustand für spätere Hinrichtungen.
    • Wenn ein Test einen Fehler anzeigt, ist der Datenbankstatus eine wichtige Information, die jedoch verloren geht (da wir vor dem nächsten Test bereinigen, um die Testsuite zu beenden und dem Entwickler eine vollständige Information anzuzeigen).

Also sind wir zu einer Richtlinie übergegangen, von der wir wussten, dass sie gültig ist:

  • haben automatische Unit-Tests für unsere Anforderungen :
    • Bei Bedarf Scheindatenbankzugriffe (aber normalerweise benötigen wir kein JMock, ich könnte unser Design erklären, wenn ich dazu aufgefordert werde).
    • Tests werden mit einer Rate von über 100 pro Sekunde ausgeführt
    • Tests sind schnell zu schreiben, kurz und klar zu lesen.
    • Tests hängen nicht von einem dauerhaften Zustand ab, daher sind sie auf natürliche Weise vollständig voneinander isoliert (kein komplexer Mechanismus erforderlich).
  • Testintegration in die Datenbank separat (dh Anforderung für Anforderung, nicht in die Anwendungslogik integriert).
    • Wir haben überlegt, diese Aufgabe zu automatisieren, aber sie schien zu kostspielig (siehe vorherigen Teil meines Beitrags).
    • Daher haben wir dieses Testhandbuch beibehalten: Von jedem Entwickler, der eine Abfrage ändert, wird erwartet, dass er die Datenbank erneut testet (dies ist normalerweise Teil seiner End-to-End-Tests über die Benutzeroberfläche).
KLE
quelle
2

Auch wenn Sie keine "Transaktionen" im T-SQL-Sinne haben, sollten Sie sich dennoch bemühen, Ihre Transaktionen (im allgemeinen Sinne des Wortes) atomar zu gestalten. Die Tests sollten nicht aufeinander angewiesen sein und reversibel sein. Wenn Sie keinen offiziellen Rollback- oder Transaktionsumfang haben, möchten Sie möglicherweise Ihren eigenen erstellen. Sie können beispielsweise Ihre Komponententests eine Bereinigung durchführen lassen, bei der alle im Test erstellten Datensätze gelöscht werden.

Morgan Herlocker
quelle
Das passiert bereits ohne Transaktion (am Ende werden die Tabellen nur geleert, da ich meine Tests immer mit leeren Tabellen beginne), aber es löst nicht das Problem, dass mehrere Tests gleichzeitig ausgeführt werden. Wenn ich Integrationstests für Anwendung A und Anwendung B gleichzeitig ausführe und beide 10 Datensätze in database.table_a einfügen, wenn ich einen Test habe, um sicherzustellen, dass 10 Datensätze in der Tabelle enthalten sind, aber ein Ergebnis von 10 erhalte, Der Test würde fehlschlagen, obwohl dieser Test funktioniert. Dies ist der Fall, den ich zu vermeiden versuche.
Ryanzec
@ryanzec - ich folge nicht ganz. Wenn Sie einen Test haben, um sicherzustellen, dass 10 Datensätze in der Tabelle enthalten sind und Sie ein Ergebnis von 10 erhalten, scheint der Test bestanden zu sein.
Morgan Herlocker
@ryanzec - Ich denke, ich könnte sehen, was du meinst. Wenn für Anwendung A 10 Datensätze in der Tabelle erforderlich sind, App B jedoch gleichzeitig einige zusätzliche Datensätze einfügt, schlägt der Test von A fehl. In diesem Fall müssen Sie Ihre Tests anders schreiben. Ich würde eine Art Transaktionsnummer angeben, damit ich nicht nach 10 Datensätzen in einer Tabelle für einen Test suche, sondern nach 10 Datensätzen mit einer zugehörigen Transaktionsnummer.
Morgan Herlocker
0

Lassen Sie einfach jeden Benutzer oder jeden Lauf die verwendete Datenbank überschreiben. Das machen wir bei der Arbeit. Dann haben Sie nie Probleme mit 2 gleichzeitigen Tests, die sich gegenseitig stören.

Wir lassen bei jedem Testlauf die Datenbank mit Migrationen aufbauen, die Datenbank mit Fixtures füllen und sie am Ende wieder abreißen.

Wenn das DBMS Transaktionen unterstützt, verwenden wir dies als Optimierung für die Ersteinrichtung und den Abbau. Sie sind optional, obwohl Ihr Test ohne sie etwas länger dauern kann. Wie immer YYMV.

Keine Aufregung, kein Muss.

Dietbuddha
quelle