Teste ich Unit-Tests oder Integrationstests meiner gespeicherten Prozeduren?

8

Ich hatte in letzter Zeit viele Gelegenheiten, in denen ich komplexe gespeicherte Prozeduren und Funktionen verwalten musste. Diese waren bereits kaputt, normalerweise auf ziemlich subtile Weise - es gab nur sehr wenige Fälle, in denen Sie den SP mit gültigen Parametern aufriefen, und es funktionierte einfach nicht.

Meine Lösung bestand darin, ein Rahmenwerk zu entwickeln, das die gespeicherten Prozeduren innerhalb einer Transaktion ausführt, nachdem die Datenbank unter den von mir benötigten Anfangsbedingungen initialisiert und dann auch innerhalb derselben Transaktion auf das erwartete Ergebnis getestet wurde. Die Transaktion wurde am Ende des Tests zurückgesetzt.

Das hat sehr gut funktioniert. Einige würden dies jedoch als "Integrationstest" bezeichnen, da es sich um die Integration in die Datenbank handelt. Ich nenne es Unit-Test, da ich einzelne Komponenten und einzelne Testfälle für diese Komponenten getestet habe und den Anfangszustand der Datenbank vollständig kontrolliert habe.

Aber wo soll die Linie gezogen werden? Handelt es sich um Integrationstests oder Unit-Tests? Gibt es einen praktischen Grund, warum diese Art von Test eine schlechte Idee ist? Wenn dies "nur" Integrationstests sind, hat jemand Vorschläge, wie tatsächliche "Unit-Tests" für diese gespeicherten Prozeduren durchgeführt werden können?


Update, 3 1/2 Jahre später. Bei meinem aktuellen Projekt habe ich mit Erfolg begonnen, SSDT-Komponententests zu verwenden, obwohl diese besser sein könnten. Siehe Überprüfen des Datenbankcodes mithilfe von SQL Server-Komponententests . Diese stellen das Datenbankprojekt normalerweise auf Ihrer Instanz von SQL Server LocalDB bereit, sodass alle Fragen zur Datenbankumgebung, die sich auf den Test auswirken, beseitigt werden. Ich fülle die Datenbank während des Vortests mit den erforderlichen Daten, wodurch Fragen zum Datenbankinhalt entfernt werden. Tatsächlich verwende ich dazu MERGE-Anweisungen, um sicherzustellen, dass alle Daten, die ich für den aktuellen Test nicht benötige, vor dem Test aus der Datenbank entfernt, eingefügt oder aktualisiert werden. Sie haben Probleme:

  • Sie sind nicht schnell
  • Es ist nicht möglich, Testbedingungen wiederzuverwenden
  • Es ist nicht möglich, Vortests wiederzuverwenden (es sei denn, Sie machen sie allen Tests in einem Projekt gemeinsam).
  • Die Benutzeroberfläche könnte verbessert werden

Einer der Gründe für die oben genannten Probleme ist, dass ich mich noch nicht darüber beschwert habe. Ich empfehle allen Interessierten, diese Funktion auszuprobieren und sich dann darüber zu beschweren. So werden Verbesserungen vorgenommen.

John Saunders
quelle
1
Warum sollte es ein Oxymoron sein ?
Doppelgreener
Gute Frage. Was halten Sie davon, den Titel zu ändern? Vielleicht so etwas wie: "Wie man gespeicherte Prozeduren
testet
@ Matthew: "kollaborativ bearbeitet"
John Saunders

Antworten:

7

Beim Unit-Test geht es nicht darum, eine magische Unit-Test-Fee dazu zu bringen, Ihre Meinung zu bestätigen. Es geht darum, die kleinsten und einfachsten Bausteine ​​Ihres Systems zu testen, damit Sie keine umfangreicheren Funktionen testen und stolpern, weil etwas nicht so funktioniert hat, wie Sie es sich vorgestellt haben. So können Sie diese brechen weiter nach unten? Ich glaube nicht, dass Sie das können. In diesem Fall:

(a) Es klingt für mich wie ein Unit-Test, und (b) Es spielt keine Rolle, ob es sich um einen Unit-Test handelt oder nicht, da es testet, was Sie testen müssen.

Wenn Sie der Meinung sind, dass die Prozeduren zu komplex sind, möchten Sie sie möglicherweise selbst in kleinere Teile aufteilen (in Datenbankprozeduren oder in Code), aber um dies zu tun, möchten Sie diese Tests natürlich zuerst durchführen!

Jack V.
quelle
7

Beim Testen von Einheiten geht es per Definition darum, eine "Codeeinheit" in ihrer laufenden Umgebung oder Plattform zu testen. Die Plattform, die Sie verwenden, ist eine Datenbank, Sie müssen sie also verwenden, sie ist nicht integrativ.

Eine bessere Frage ist, warum es dich interessiert. Ist es wichtig, dass Ihr Test in einem Unit-Test, einem Abnahmetest, einem Rauchtest oder einem Funktionstest durchgeführt wird, solange er automatisiert ist und einen Mehrwert bietet?

Bei meiner Arbeit nutzen wir derzeit das Unit-Testing-Framework, um Acceptance Test Driven Development (ATDD) durchzuführen. Einige Tests sind integrativ, andere nicht. Fast keiner von ihnen ist ein Unit-Test, aber es ist uns egal, solange:

  • Der Test schafft Mehrwert
  • Der Test hat keine falschen Fehler oder Erfolge (meistens deterministisch).
  • Der Test ist automatisiert

Aktualisieren

Es ist alles eine Frage von Kosten und Nutzen. Je mehr bewegliche Teile Sie haben, desto höher ist das Risiko eines nicht deterministischen Tests. Klassische Unit-Tests haben die geringste Anzahl beweglicher Teile und daher die geringste Wahrscheinlichkeit, nicht deterministisch zu sein. Der Nachteil ist, dass Sie die Integrationspunkte (Code, Datenbank, fs, Netzwerk usw.) nicht testen. All dies ist nützlich, um in einem Test behandelt zu werden, solange das Risiko eines falschen Scheiterns oder Erfolgs gering genug ist.

Tests liefern Wert in dem, was sie tun, nicht was sie sind. Der Schlüssel ist, wie oft Sie falsche Fehler und Erfolge haben werden. Wenn ich jeden Monat 2 Stunden lang falsche Fehler habe, weil dies ein Wartungsfenster für das Netzwerk ist, ist der Wert der Tests offensichtlich. Wenn sie fehlschlagen, ist offensichtlich, dass mit der Netzwerkressource etwas nicht stimmt, nicht mit einem bestimmten Test. Und jetzt haben Sie mehr Testabdeckung, gerade weil Sie diese Integrationspunkte testen.

Dietbuddha
quelle
Einer der Gründe, die mich interessieren, ist, dass sich einige Orte beschweren, wenn Ihre Unit-Tests per Definition keine "Unit-Tests" sind. Ein weiterer Grund ist, dass es in Kommentaren zu " Existiert ein Mittelweg? (Unit Testing vs. Integration Testing) "
John Saunders
@John Basierend auf der Antwort von @ dietbuddha und den Recherchen, die ich gelesen habe: Da Sie den Netzwerkstapel und den DB-Server verwenden, sind Ihre Tests nicht "meistens deterministisch". Sie sollten nicht als Unit-Tests eingestuft werden. Aber das ist natürlich nur meine Meinung.
Matthew Rodatus
1
@Matthew: Ich weiß nicht, auf welchen Plattformen Sie entwickeln, aber ich garantiere Ihnen, dass der von mir verwendete Netzwerkstapel und Datenbankserver nicht sehr oft ausfällt. Tatsächlich können Fehler dieser Komponenten zum Testen ignoriert werden.
John Saunders
@ John Saunders: aktualisierte Antwort
Dietbuddha
2

Ich denke, es kommt darauf an, woher die Daten kommen. Wenn Sie Ihre Testbedingungen im Testpräfix erstellen, ist es meiner Meinung nach sinnvoll, dies als Komponententest zu bezeichnen. Andernfalls stehen Sie in wählerischen Auseinandersetzungen darüber, was für einen Komponententest als "bereits vorhanden" gilt. So wie Ihre Datenbank-Unit-Tests auf der Existenz von Datenbanktabellen usw. beruhen, beruhen Unit-Tests außerhalb der Datenbank auf Klassendefinitionen und häufig auf Instanzen dieser Klassen. Das ist nicht schlecht, es ist realistisch.


quelle
1

Ich würde das als Integrationstest betrachten:

  1. Es ist viel langsamer als ein Unit-Test sein sollte.
  2. Es wird keine einzelne atomare Codeeinheit ausgeführt - es wird Ihre Prozedur, der Datenbankserver, der Netzwerkstapel usw. ausgeführt.
  3. Es ist nicht leicht zu erkennen, wo der Fehler aufgetreten ist, wenn der Test fehlschlägt. Sie müssen jeden Fehler manuell überprüfen, um festzustellen, ob es sich um einen externen Systemfehler oder um Ihren Code handelt.

Tests, wie Sie sie beschreiben, sind jedoch von unschätzbarem Wert. Wir haben kürzlich damit begonnen, einfach solche Tests hinzuzufügen, um gespeicherte Prozeduren zu testen, die in bestimmten Umgebungen nicht manuell getestet werden können. Ich kenne keinen anderen Weg, um automatisierte Tests für gespeicherte Prozeduren durchzuführen.

Diese Tests sind also eine großartige Idee, aber nennen Sie sie Integrationstests. Verwenden Sie eine Testkategorie oder ein Namenssuffix, um sie von regulären Komponententests zu unterscheiden. Auf diese Weise können Sie Integrationstestfehler anders als Unit-Testfehler in Ihrer Build-Automatisierung kennzeichnen.

Matthew Rodatus
quelle
@Matthew: Ich teste den Datenbankserver oder den Netzwerkstapel nicht mehr als die .NET BCL oder CLR. Außerdem hatte ich überhaupt keine Probleme beim Lokalisieren von Fehlern. Vielleicht sollte ich ein detaillierteres Beispiel für die Art von Test geben, die ich meine.
John Saunders
@John Als ich sagte "Teste den Datenbankserver und den Netzwerkstapel", meine ich, dass du jeden Code testest, den du trainierst. Die .NET BCL / CLR muss ein Ticket für den Unit-Test haben - ohne sie können Sie keinen Code schreiben! Bei einem Test, der mit einem Datenbankserver kommuniziert, werden jedoch auch der Server und das Netzwerk getestet (dh getestet). Ich denke, ich sollte fragen: Wie führen Sie Ihre Prozedurtests durch? Von einer C # -Einheitentestmethode? Ein SQL-Skript? Wie automatisieren Sie die Ergebnisberichterstattung?
Matthew Rodatus
@Matthew: Ein normaler NUnit-Test einer Methode in meiner Klasse führt nach Ihrer Definition das .NET Framework aus. Nicht von mir, da ich davon ausgehe, dass .NET funktioniert. Ebenso gehe ich davon aus, dass SQL Server funktioniert und dass die SqlCommandKlasse funktioniert und dass der TCP-Transport funktioniert. Da habe ich mich noch nicht geirrt.
John Saunders
1
@Matthew: Wenn SQL Server und seine integrierten Transporte und Funktionen nicht funktionieren, habe ich größere Probleme als beim Testen von Einheiten oder beim Testen von Integrationen. Ich gehe normalerweise davon aus, dass das Betriebssystem ebenfalls funktioniert und dass die Lichtgeschwindigkeit innerhalb der Frames, in denen meine Tests ausgeführt werden, mehr oder weniger konstant ist.
John Saunders
1
@ John Ich glaube nicht, dass du meinen Standpunkt verstehst, aber na ja.
Matthew Rodatus
0

Handelt es sich um Integrationstests oder Unit-Tests?

Wo läuft der Test? In der Datenbank oder über einen codebasierten Testkabelbaum (JUnit)?

In der Datenbank handelt es sich dann wahrscheinlich um einen Komponententest (da dieser in sich geschlossen ist), ein JUnit-ähnliches Testkabel, dann wird eine Verbindung zu einer externen Datenbank hergestellt und als solcher ein Integrationstest durchgeführt.

Wenn dies "nur" Integrationstest ist

Warum die Zitate? Integrationstests sind keine Bestie, die Sie niemals tun sollten.

Hat jemand Vorschläge, wie tatsächliche "Unit-Tests" für diese gespeicherten Prozeduren durchgeführt werden können?

Ich finde im Allgemeinen, dass die einzige vernünftige Einheit für eine gespeicherte Prozedur der aufrufende Code¹ und die Prozedur ist. Also teste ich den kompletten Satz in einer Reihe von Tests, die genau wie Unit-Tests aussehen.

Eine bessere Frage ist, warum Sie sich [um den Namen] kümmern.

Da können Sie die Tests aufteilen. Die Ausführung von Integrationstests dauert länger, erfordert zusätzliche Einrichtung und wahrscheinlich Zugriff auf eine Ressource, die von vielen Tests gemeinsam genutzt wird (z. B. die Datenbank).

1) Der aufrufende Code enthält bei Bedarf zusätzliche Komponententests.

mlk
quelle
Es gibt kein zusätzliches Setup. Das gesamte Setup befindet sich im Test selbst. Auch diese Tests sind ziemlich schnell. Die Vorbereitung in der Testpräambel musste bisher nicht mehr als 50 Zeilen löschen und dann ein weiteres Dutzend oder so einfügen.
John Saunders
Es startet eine Datenbank, wenn eine nicht läuft?
mlk
"Startet eine Datenbank"? Nein, die Tests verwenden eine Konfigurationszeichenfolge, die in der Konfigurationsdatei für die Tests gespeichert ist, und man geht davon aus, dass nach Festlegung der richtigen Konfigurationszeichenfolge weiterhin auf eine gültige Datenbank innerhalb eines gültigen Datenbankservers verwiesen wird. In einer idealen Welt können diese Tests nach einer Bereitstellung (Erstellung) einer neuen Instanz der Datenbank unter Verwendung der aktuellen DDL ausgeführt werden, die ebenfalls in der Quellcodeverwaltung gespeichert ist. Wir arbeiten auf dieses Ideal hin.
John Saunders
Es ist ein gutes Ideal, um dorthin zu gelangen. Ich arbeite seit einiger Zeit darauf hin. Mein Punkt war, dass Sie Startaufgaben haben (eine Datenbank starten). Sie können (und sollten) dafür sorgen, dass Ihre Tests nicht fehlschlagen (Assert.Inconclusive in MSTest speak), wenn die Aufgaben nicht ausgeführt wurden. Dieser Bereich zum Testen einer Grauzone zum Testen von Einheiten. Schreiben Sie Unit-Test-ähnliche Tests, akzeptieren Sie jedoch, dass es sich um Integrationstests handelt.
mlk
1
Ich bin mir nicht sicher, was Sie unter dem Starten einer Datenbank verstehen. In meiner "idealen Welt" wäre die Bereitstellung einer neuen, leeren Instanz der Datenbank Teil des automatisierten Builds und nicht Teil der Tests.
John Saunders
-1

Mikrotest ist ein nützlicher Begriff, der Ihnen helfen kann, die Debatte über die Terminologie zu umgehen und ob Ihr Test ein Komponententest ist oder nicht.

Eine gebräuchliche Definition von Unit-Test beinhaltet, dass das zu testende System eine kleine Funktionseinheit ist und Sie in der Lage sein müssen, alle Tests im Speicher ohne Hilfe von Dateisystemen, Netzwerken oder Datenbank-Engines auszuführen . Wenn Sie ein tatsächliches Datenbankmodul benötigen, um es auszuführen, handelt es sich nicht um einen Komponententest.

Diese Definition funktioniert zwar sehr gut für alle Anwendungsebenen, ist jedoch für die unterste Datenbankzugriffsebene nicht sehr hilfreich. Wir müssen erkennen, dass es etwas Besonderes ist. Wenn Ihre Tests nur kleine Einheiten der Speicher- / Abruf- / Aktualisierungs- / Löschfunktion testen, sind Ihre Tests eindeutig genauso wertvoll wie "strenge" Komponententests, die für die oberen Schichten Ihres Systems geschrieben wurden.

Azheglov
quelle
Hier besteht die Kontroverse: Ich teste nicht die DAL: Ich teste die gespeicherten Prozeduren und Funktionen in der Datenbank selbst. Wenn der DAL so einfach ist, wie nur Parameter einzurichten und dann den SP oder die Funktion aufzurufen, oder SELECT, dann mache ich mir nicht die Mühe, ihn zu testen.
John Saunders