Zu Ihrer Information: Dieser Artikel ist eine erweiterte Version meines Berichts über die SQA-Tage Nr. 25.Aufgrund meiner Erfahrung in der Kommunikation mit Kollegen kann ich sagen, dass das Testen von Code in einer Datenbank keine gängige Praxis ist. Dies kann eine potenzielle Gefahr sein. Die Logik in der Datenbank wird von denselben Personen geschrieben, die den "regulären" Code schreiben. Folglich können dort auch Fehler vorhanden sein, die auch negative Folgen für das Produkt, das Geschäft und die Verbraucher haben können. Es spielt keine Rolle, ob es sich um gespeicherte Prozeduren handelt, die dem Backend helfen, oder um ETLs, die Daten in Speicher konvertieren - es besteht ein Risiko, und Tests können es erheblich reduzieren. Ich möchte Ihnen sagen, was tSQLt ist und wie es uns beim Testen von Code in SQL Server hilft.
Kontext
Auf SQL Server gibt es ein großes Lager, das verschiedene klinische Forschungsdaten enthält. Es wird aus verschiedenen Quellen (hauptsächlich dokumentenorientierten Datenbanken) gefüllt. Innerhalb des Servers selbst werden Daten wiederholt mit ETL konvertiert. In Zukunft können diese Daten in kleinere Datenbanken hochgeladen werden, um von Webanwendungen verwendet zu werden, die einige kleine spezifische Probleme lösen. Einige Kunden des Kunden fragen auch nach einer API für ihre internen Anforderungen. Bei der Implementierung solcher APIs werden häufig gespeicherte Prozeduren und Abfragen verwendet.
Im Allgemeinen befindet sich der Code in der Reihenfolge auf der DBMS-Seite.
Warum das alles gebraucht wird
Wie bereits aus der Einführung verstanden, ist der Code in der Datenbank derselbe Code
Anwendungen, wie der Rest, und es kann auch Fehler geben.
Ich denke, viele Menschen sind sich der Abhängigkeit des Preises des Fehlers vom Zeitpunkt seiner Entdeckung bewusst, dessen Entdeckung normalerweise Barry Bohem zugeschrieben wird. Ein Fehler, der in einem frühen Stadium gemacht und zu einem späteren Zeitpunkt erkannt wird, kann teurer sein, da viele Phasen (Codierung, Einheit, Integration, Systemtests usw.) wiederholt durchlaufen werden müssen, um den Fehler zu lokalisieren und den korrigierten Code wieder herzustellen das Stadium, in dem das Problem identifiziert wurde. Dieser Effekt ist auch für den Lagerfall relevant. Wenn sich ein Fehler in eine ETL eingeschlichen hat und die Daten mehrere Transformationen durchlaufen, müssen Sie: Wenn in den Daten ein Fehler festgestellt wird, Folgendes tun:
- Führen Sie alle Schritte der Konvertierung zurück zur Lokalisierung des Problems.
- Beheben Sie das Problem.
- Erhalten Sie die korrigierten Daten erneut (manuelle Korrekturen sind nicht ausgeschlossen).
- Stellen Sie sicher, dass durch den Fehler verursachte falsche Daten nicht an anderer Stelle angezeigt wurden.
Vergessen Sie nicht, dass wir kein Stofftier verkaufen. Ein Fehler in einem Bereich wie der klinischen Forschung kann nicht nur das Geschäft, sondern auch die Gesundheit der Menschen schädigen.
Wie teste ich?
Da es sich um Codetests handelt, meinen wir Unit- und Integrationstests. Diese Dinge sind sehr probenhaft und beinhalten eine ständige Regression. Genau genommen führt niemand solche Tests manuell durch (naja, vielleicht mit Ausnahme einiger völlig entarteter Fälle).
Ein schöner Bonus: Tests können ein Hilfsmaterial bei der Dokumentation des Codes sein. Kundenanforderungen können übrigens so aussehen (anklickbar):
Excel, zwei Spalten mit Anforderungen + verstreute unterstützende Informationen in anderen Spalten + verschwommenes Markup, was verwirrender ist als es hilft. Falls erforderlich, kann es schwierig sein, die ursprünglichen Wünsche wiederherzustellen. Tests können dabei helfen, die Nuancen der Implementierung genauer zu erfassen (natürlich sollten Sie sie nicht als Äquivalent zur Dokumentation betrachten).
Leider wächst mit der Komplexität des Codes die Komplexität der Tests, und dieser Effekt kann ausgeglichen werden.
Tests können als zusätzliche Sicherheitsebene gegen spontane Walrosse dienen. Selbsttests in CI aufgrund von Formalismus helfen, dieses Problem zu bewältigen.
Wenn unsere Wahl auf den Weg der Automatisierung fiel, müssen wir uns für die Tools für deren Implementierung entscheiden.
Wie teste ich?
Beim Testen des Codes in der Datenbank unterscheide ich zwei Ansätze: SQL-basiert, d. H. Direkt im DBMS funktionieren, und Nicht-SQL-unterstützt. Ich konnte folgende Nuancen hervorheben:
In SQL Server haben wir eine Auswahl:
Bewertungen von "gut-schlecht" sind subjektiv, sorry, ohne dies nirgendwo.
Erläuterung: „Erstes Erscheinen“ ist das früheste Datum im Lebensweg des Frameworks, das ich gefunden habe, dh die früheste Veröffentlichung oder Festschreibung.
Möglicherweise stellen Sie fest, dass SQL-basierte Alternativen seit einiger Zeit aufgegeben wurden und tSQLt die einzige unterstützte Option ist. Funktionell gewinnt auch tSQLt. Das einzige ist, dass TST in Bezug auf Behauptungen eine etwas reichhaltigere Auswahl bietet als tSQLt, was jedoch wahrscheinlich nicht die Nachteile überwiegt.
Es gibt Nuancen in der tSQLt-Dokumentation, aber ich werde später darüber sprechen.
In der Welt ohne SQL sind die Dinge nicht so einfach. Alternativen, wenn auch nicht sehr aktiv, werden entwickelt. DbFit ist ein interessantes Tool, das auf dem FitNesse-Framework basiert. Er bietet Schreibtests zum Wiki-Markup an. Slacker ist auch eine merkwürdige Sache - der BDD-Ansatz beim Schreiben von Tests für die Datenbank.
Ich werde die Behauptungen in nicht SQL-basierten diskutieren. Äußerlich gibt es weniger von ihnen, und man könnte sagen, dass sie aus diesem Grund schlechter sind. Hier ist jedoch zu berücksichtigen, dass sie sich grundlegend von tSQLt unterscheiden. Nicht alles ist so einfach.
Die letzte Zeile lautet "NUnit usw." - Es ist eher eine Erinnerung. Viele der in der täglichen Arbeit bekannten Unit-Testing-Frameworks können mithilfe von Zusatzbibliotheken in Hilfsdatenbanken verwendet werden. Die Tabelle enthält viele N / A, da diese Zeile tatsächlich viele Tools enthält. Die „Nuancen“ in der Assertionsspalte stammen aus demselben Punkt - in verschiedenen Tools kann ihre Menge variieren, und die Frage der Anwendbarkeit auf die Datenbank ist offen.
Als weitere interessante Metrik können wir
Google-Trends berücksichtigen.
Nuancen:
- Ich habe Slacker nicht aufgenommen, da dieser Name viele Dinge bedeuten kann (und Anfragen wie "Slacker-Framework" in den Diagrammen nicht besonders sichtbar sind).
- Aus Neugier wurde der TST-Trend hinzugefügt, aber er spiegelt auch nicht viel den Stand der Dinge wider, da es sich um eine Abkürzung handelt, die viele verschiedene Dinge bedeutet.
- Ich habe NUnit und seine Analoga nicht berücksichtigt, da dies ursprünglich Frameworks zum Testen des Anwendungscodes selbst waren und ihre Trends nicht auf unseren Kontext hinweisen.
Im Allgemeinen können wir sagen, dass tSQLt vor dem Hintergrund von Analoga positiv aussieht.
Was ist tSQLt?
tSQLt ist, wie Sie sich vorstellen können, ein SQL-basiertes Unit-Testing-Framework.
→
Offizielle SeiteDie SQL Server-Unterstützung wurde seit 2005 SP2 angekündigt. Ich konnte noch nie so weit in die Vergangenheit schauen, aber wir haben 2012 auf dem Dev-Server, ich habe lokal 2017 - es gab keine Probleme.
Open Source, eine Apache 2.0-Lizenz,
ist auf GitHub verfügbar . Sie können in kommerziellen Projekten gabeln, schmuggeln, kostenlos verwenden und vor allem keine Angst vor Lesezeichen in der CLR haben.
Arbeitsmechaniker
Testfälle sind gespeicherte Prozeduren. Sie werden zu Testklassen zusammengefasst (Testsuite in Bezug auf xUnit).
Testklassen sind nichts anderes als normale Datenbankschemata. Sie unterscheiden sich von anderen Schemata durch die Registrierung in den Rahmentabellen. Sie können eine solche Registrierung vornehmen, indem Sie eine Prozedur aufrufen - tSQLt.NewTestClass.
Innerhalb der Testklasse kann auch eine SetUp-Prozedur definiert werden, die ausgeführt wird, bevor jeder einzelne Testfall ausgeführt wird.
Ein Abbauverfahren zur Wiederherstellung des Systems nach Abschluss des Testfalls ist nicht erforderlich. Jeder Testfall wird zusammen mit der SetUp-Prozedur in einer separaten Transaktion ausgeführt, die nach dem Sammeln der Ergebnisse zurückgesetzt wird. Dies ist sehr praktisch, hat jedoch einige negative Auswirkungen, auf die ich weiter unten eingehen werde.
Mit dem Framework können Sie einzelne Testfälle, ganze Testklassen oder alle registrierten Testklassen gleichzeitig ausführen.
Funktionen am Beispiel
Da ich keine Lust habe, einen bereits einfachen offiziellen Leitfaden noch einmal zu erzählen, werde ich anhand von Beispielen über die Möglichkeiten des Frameworks sprechen.
Haftungsausschluss:- Beispiele werden vereinfacht;
- im Original wurde nicht der gesamte Testcode von mir geschrieben, sondern es sind eher die Früchte der kollektiven Kreativität;
- Beispiel 2 wurde erfunden, um die Fähigkeiten von tSQLt besser zu demonstrieren.
Beispiel 1: CsvSql
Auf Wunsch eines Kunden des Kunden wurde Folgendes implementiert. Die Datenbank in Nvarchar (MAX) -Feldern speichert SQL-Abfragen. Um diese Abfragen anzuzeigen, wird ein minimales Frontend verschraubt. Die von diesen Anforderungen zurückgegebenen Ergebnismengen werden vom Backend verwendet, um die CSV-Datei für die Rückgabe über den API-Aufruf zu generieren.
Ergebnissätze sind ziemlich schwer und enthalten viele Spalten. Ein bedingtes Beispiel für eine solche Ergebnismenge:
Diese Ergebnismenge enthält einige Daten aus klinischen Studien. Schauen wir uns genauer an, wie ClinicsNum betrachtet wird - die Anzahl der an der Studie beteiligten Kliniken. Wir haben zwei Tabellen: [Studie] und [Klinik]:
Es gibt FK: [Klinik]. [TrialID] -> [Trial]. [TrialID]. Um die Anzahl der Kliniken zu berechnen, benötigen wir natürlich nur die übliche COUNT (*).
SELECT COUNT(*), ... FROM dbo.Trial LEFT JOIN dbo.Clinic ON Trial.ID = Clinic.TrialID WHERE Trial.Name = @trialName GROUP BY ...
Wie testen wir eine solche Anfrage? Zunächst können wir den praktischen Stub FakeTable verwenden, der die weitere Arbeit erheblich vereinfacht.
EXEC tSQLt.FakeTable 'dbo.Trial'; EXEC tSQLt.FakeTable 'dbo.Clinic';
FakeTable macht eine einfache Sache - benennt alte Tabellen um und erstellt an ihrer Stelle neue. Dieselben Namen, dieselben Spalten, aber ohne Einschränkung'ov und Trigger'ov.
Warum brauchen wir das:
- Möglicherweise enthält die Testdatenbank einige Daten, die die Tests beeinträchtigen können. Dank FakeTable sind wir nicht von ihnen abhängig.
- Für den Test müssen Sie in der Regel nur wenige Spalten ausfüllen. Es können viele von ihnen in der Tabelle sein, und einige von ihnen haben Einschränkungen. Auf diese Weise vereinfachen wir die weitere Installation von Testdaten - wir fügen nur diejenigen ein, die für den Testfall wirklich notwendig sind.
- Wir haben definitiv keinen Einfluss auf den Auslöser beim Einfügen von Testdaten und müssen uns keine Gedanken über unerwünschte Nachwirkungen machen.
Fügen Sie als Nächstes die benötigten Testdaten ein:
INSERT INTO dbo.Trial ([ID], [Name]) VALUES (1, 'Valerian'); INSERT INTO dbo.Clinic ([ID], [TrialID], [Name]) VALUES (1, 1, 'Clinic1'), (2, 1, 'Clinic2');
Wir erhalten unsere Abfrage aus der Datenbank, erstellen die Ist-Tabelle und füllen sie mit der Ergebnismenge unserer Abfrage:
DECLARE @sqlStatement NVARCHAR(MAX) = (SELECT… CREATE TABLE actual ([TrialID], ...); INSERT INTO actual EXEC sp_executesql @sqlStatement, ...
Füllen Sie die erwarteten - erwarteten Werte aus:
CREATE TABLE expected ( ClinicsNum INT ); INSERT INTO expected SELECT 2
Ich möchte Ihre Aufmerksamkeit auf die Tatsache lenken, dass wir in der Tabelle "Erwartet" nur eine Spalte haben, während wir in "Tatsächlich" einen vollständigen Satz haben.
Dies liegt an der Funktion der AssertEqualsTable-Prozedur, mit der wir die Werte überprüfen.
EXEC tSQLt.AssertEqualsTable 'expected', 'actual', 'incorrect number of clinics';
Es werden nur die Spalten verglichen, die in beiden zu vergleichenden Tabellen vorhanden sind. Dies ist in unserem Fall sehr praktisch, da die Testabfrage viele Spalten zurückgibt, von denen jede ihre eigene Logik hat, die manchmal sehr verwirrend ist. Wir möchten keine Testfälle aufblasen, daher ist diese Funktion für uns sehr nützlich. Natürlich ist dies ein zweischneidiges Schwert. Wenn in Actual eine Reihe von Spalten automatisch über SELECT TOP 0 gefüllt wird und irgendwann plötzlich eine zusätzliche Spalte angezeigt wird, wird ein solcher Testfall diesen Moment nicht erfassen. Um solche Situationen zu bewältigen, müssen Sie zusätzliche Überprüfungen durchführen.
Schwesterprozeduren AssertEqualsTable
Es ist erwähnenswert, dass tSQLt zwei Prozeduren enthält, die AssertEqualsTable ähnlich sind. Dies sind AssertEqualsTableSchema und AssertResultSetsHaveSameMetaData. Der erste Vorgang entspricht dem von AssertEqualsTable, jedoch für die Tabellenmetadaten. Der zweite macht einen ähnlichen Vergleich, aber auf den Metadaten der Ergebnismengen.
Beispiel 2: Einschränkungen
Im vorherigen Beispiel haben wir gesehen, wie Sie Einschränkungen entfernen können. Aber was ist, wenn wir sie überprüfen müssen? Technisch gesehen ist dies auch Teil der Logik und kann auch als Kandidat für die Testabdeckung angesehen werden.
Betrachten Sie die Situation aus dem vorherigen Beispiel. Zwei Tabellen - [Studie] und [Klinik]; [TrialID] FK:
Versuchen wir, einen Testfall zu schreiben, um diese Einschränkung zu testen. Zuerst, wie beim letzten Mal, fälschen wir Tische.
EXEC tSQLt.FakeTable '[dbo].[Trial]' EXEC tSQLt.FakeTable '[dbo].[Clinic]'
Das Ziel ist das gleiche - unnötige Einschränkungen zu beseitigen. Wir wollen isolierte Kontrollen ohne unnötige Gesten.
Als Nächstes geben wir die benötigte Einschränkung mithilfe der ApplyConstraint-Prozedur an den Ort zurück:
EXEC tSQLt.ApplyConstraint '[dbo].[Clinic]', 'Trial_FK';
Hier haben wir eine praktische Konfiguration für die direkte Überprüfung zusammengestellt. Die Prüfung selbst besteht darin, dass ein Versuch, Daten einzufügen, zwangsläufig zu einer Ausnahme führt. Damit der Testfall korrekt funktioniert, müssen wir genau diese Ausnahme abfangen. Der ExpectException-Ausnahmebehandler hilft uns dabei.
EXEC tSQLt.ExpectException @ExpectedMessage = 'The INSERT statement conflicted...', @ExpectedSeverity = 16, @ExpectedState = 0;
Nach der Installation des Handlers können Sie versuchen, das nicht einfügbare einzufügen.
INSERT INTO [dbo].[Clinic] ([TrialID]) VALUES (1)
Ausnahme abgefangen. Test bestanden.
Schwesterprozeduren ApplyConstraint
TSQLt-Entwickler bieten uns einen ähnlichen Ansatz zum Testen von Triggern an. Mit der ApplyTrigger-Prozedur können Sie einen Trigger an die gefälschte Tabelle zurückgeben. Außerdem ist alles wie im obigen Beispiel - wir lösen den Trigger aus und überprüfen das Ergebnis.
ExpectNoException - das Antonyme von ExpectException
In Fällen, in denen eine Ausnahme definitiv nicht auftreten sollte, gibt es eine ExpectNoException-Prozedur. Es funktioniert genauso wie eine ExpectException, außer dass der Test als fehlgeschlagen betrachtet wird, wenn eine Ausnahme auftritt.
Beispiel 3: Semaphor
Die Situation ist wie folgt. Es gibt eine Reihe von gespeicherten Prozeduren und Windows-Diensten. Der Beginn ihrer Ausführung kann durch verschiedene externe Ereignisse verursacht werden. In diesem Fall ist die zulässige Reihenfolge ihrer Ausführung festgelegt. Ein Semaphor wird verwendet, um den Zugriff auf Datenbanktabellen zu unterscheiden. Es ist eine Gruppe gespeicherter Prozeduren.
Betrachten Sie beispielsweise eines dieser Verfahren. Wir haben zwei Tabellen:
Die Tabelle [Prozess] enthält eine Liste der zur Ausführung zugelassenen Prozesse, [ProcStatus] - eine Liste der Status dieser Prozesse.
Was macht unser Verfahren? Beim Aufruf erfolgt zunächst eine Reihe von Überprüfungen:
- Der Name des zu startenden Prozesses, der im Prozedurparameter übergeben wird, wird im Feld [Name] der Tabelle [Prozess] nachgeschlagen.
- Wenn der Prozessname gefunden wurde, wird geprüft, ob es derzeit möglich ist, ihn zu starten - das Flag [IsRunable] der Tabelle [Process].
- Wenn sich herausstellt, dass der Prozess für die Ausführung akzeptabel ist, muss sichergestellt sein, dass er noch nicht ausgeführt wird. In der Tabelle [ProcStatus] wird das Fehlen von Datensätzen zu diesem Prozess mit dem Status = 'InProg' überprüft.
Wenn alles in Ordnung ist, wird ProcStatus ein neuer Datensatz mit dem Status 'InProg' hinzugefügt (dies wird als Start betrachtet). Die ID dieses Datensatzes wird mit dem Parameter ProcStatusId zurückgegeben. Wenn eine Überprüfung fehlschlägt, erwarten wir Folgendes:
- Ein Brief wird an den Systemadministrator gesendet.
- Gibt ProcStatusId = -1 zurück.
- Ein neuer Eintrag in [ProcStatus] wird nicht hinzugefügt.
Schreiben wir einen Testfall, um den Fall zu testen, wenn der Prozess nicht in der Liste der akzeptablen Fälle enthalten ist.
Wenden Sie der Einfachheit halber sofort FakeTable an. Hier ist es nicht so grundlegend wichtig, aber es kann nützlich sein:
- Wir werden garantiert alle Daten entfernen, die die korrekte Ausführung des Testfalls beeinträchtigen könnten.
- Wir werden die weitere Überprüfung fehlender Einträge in ProcStatus vereinfachen.
EXEC tSQLt.FakeTable 'dbo.Process'; EXEC tSQLt.FakeTable 'dbo.ProcStatus';
Zum Senden einer Nachricht wird die von unseren Programmierern geschriebene Prozedur [SendEmail] verwendet. Um das Senden eines Briefes an Administratoren zu überprüfen, müssen wir ihren Anruf entgegennehmen. In diesem Fall bietet uns tSQLt die Verwendung von SpyProcedure an.
EXEC tSQLt.SpyProcedure 'dbo.SendEmail'
SpyProcedure führt Folgendes aus:
- Erstellt eine Tabelle der Form [dbo]. [SendEmail_SpyProcedureLog].
- Wie FakeTable ersetzt es die ursprüngliche Prozedur durch eine eigene mit demselben Namen, die jedoch eine Protokollierungslogik enthält. Falls gewünscht, können Sie eine beliebige eigene Logik hinzufügen.
Wie Sie vielleicht erraten haben, erfolgt die Protokollierung in der Tabelle [dbo]. [SendEmail_SpyProcedureLog]. Diese Tabelle enthält die Spalte [_ID_] - die Sequenznummer des Prozeduraufrufs. Die nachfolgenden Spalten tragen die Namen der Parameter, die an die Prozedur übergeben wurden, und die in den Aufrufen übergebenen Werte werden in ihnen gesammelt. Bei Bedarf können sie auch überprüft werden.
Das letzte, was wir tun müssen, bevor wir das Semaphor aufrufen und prüfen, ist, eine Variable zu erstellen, in die wir die Datensatz-ID aus der [ProcStatus] -Tabelle einfügen (genauer -1, da der Datensatz nicht hinzugefügt wird).
DECLARE @ProcStatusId BIGINT;
Nennen Sie das Semaphor:
EXEC dbo.[Semaphore_JobStarter] 'SomeProcess', @ProcStatusId OUTPUT;
Jetzt haben wir alle notwendigen Daten zur Überprüfung. Beginnen wir mit der Überprüfung der Sendung.
Buchstaben:
IF NOT EXISTS ( SELECT * FROM dbo.SendEmail_SpyProcedureLog) EXEC tSQLt.Fail 'SendEmail has not been run.';
In diesem Fall haben wir beschlossen, die während des Sendens übertragenen Parameter nicht zu überprüfen, sondern nur die Tatsache selbst zu überprüfen. Ich mache Sie auf das Verfahren tSQLt.Fail aufmerksam. Damit können Sie den Testfall "offiziell" nicht bestehen. Wenn Sie eine bestimmte Konstruktion erstellen müssen, können Sie dies mit tSQLt.Fail tun.
Überprüfen Sie als Nächstes das Fehlen von Einträgen in [dbo]. [ProcStatus]:
EXEC tSQLt.AssertEmptyTable 'dbo.ProcStatus';
Hier hat uns die FakeTable geholfen, die wir zu Beginn angewendet haben. Dank ihm können wir Leere erwarten. Ohne sie sollten wir zur genauen Überprüfung die Anzahl der Datensätze vor und nach dem Semaphor auf gute Weise vergleichen.
Gleichheit ProcStatusId = -1 können wir leicht mit AssertEquals überprüfen:
EXEC tSQLt.AssertEquals -1, @ProcStatusId, 'Wrong ProcStatusId.';
AssertEquals ist minimalistisch - es vergleicht einfach zwei Werte, nichts Übernatürliches.
AssertEquals Geschwisterverfahren
Um die Werte zu vergleichen, stehen uns eine Reihe von Verfahren zur Verfügung:
- AssertEquals
- AssertNotEquals
- AssertEqualsString
- Assertike
Ich denke, ihre Namen sprechen für sich. Das einzige, was erwähnenswert ist, ist das Vorhandensein einer separaten AssertEqualsString-Prozedur. Die Sache ist, dass AssertEquals / AssertNotEquals / AssertLike mit SQL_VARIANT zusammenarbeiten und NVARCHAR (MAX) nicht darauf anwendbar ist. Daher mussten tSQLt-Entwickler ein separates Verfahren zum Testen von NVARCHAR (MAX) zuweisen.
Gefälschte Funktion
FakeFunction mit einer gewissen Dehnung kann als eine Prozedur bezeichnet werden, die SpyProcedure ähnelt. Mit dieser Fälschung können Sie jede Funktion durch die notwendige einfachere ersetzen. Da die Funktionen in SQL Server nach dem Prinzip einer Tube mit Zahnpasta arbeiten - sie liefern das Ergebnis durch das "einzige technologische Loch" -, wird leider keine Protokollierungsfunktion bereitgestellt. Nur ein Ersatz für Logik.
Fallstricke
Es ist erwähnenswert, dass einige Fallstricke auftreten können, wenn Sie mit tSQLt arbeiten. In diesem Fall meine ich mit Fallstricken einige problematische Probleme, die aufgrund der Einschränkungen von SQL Server entstanden sind und / oder von den Entwicklern des Frameworks nicht gelöst werden können.
Stornierung / Beschädigung von Transaktionen
Das erste und wichtigste Problem unseres Teams war die Stornierung von Transaktionen. SQL Server weiß nicht, wie verschachtelte Transaktionen separat zurückgesetzt werden sollen - nur alles als Ganzes bis hin zu den externesten. Angesichts der Tatsache, dass tSQLt jeden Testfall in eine separate Transaktion einschließt, wird dies zu einem Problem. Schließlich kann ein Transaktions-Rollback innerhalb der Testprozedur die Testausführung unterbrechen und einen nicht beschreibenden Ausführungsfehler verursachen.
Um dieses Problem zu umgehen, verwenden wir Sicherungspunkte. Die Idee ist einfach. Bevor wir eine Transaktion im Testverfahren starten, prüfen wir, ob wir uns bereits in der Transaktion befinden. Wenn sich herausstellt, dass dies der Fall ist, setzen wir unter der Annahme, dass es sich um eine tSQLt-Transaktion handelt, den Sicherungspunkt, anstatt ihn zu starten. Bei Bedarf kehren wir dann zu diesem Sicherungspunkt zurück und nicht zum Beginn der Transaktion. Verschachteln als solches ist nicht.
Das Problem wird durch Transaktionsbeschädigung kompliziert. Wenn plötzlich etwas schief gelaufen ist und die Ausnahme funktioniert hat, ist die Transaktion möglicherweise zum Scheitern verurteilt. Eine solche Transaktion kann nicht nur festgeschrieben, sondern auch auf den Sicherungspunkt zurückgesetzt werden, sondern nur das Ganze zurückgesetzt werden.
In Anbetracht all dieser Punkte müssen Sie das folgende Design anwenden:
DECLARE @isNestedTransaction BIT = CASE WHEN @@trancount > 0 THEN 'true' ELSE 'false' END; BEGIN TRY IF @isNestedTransaction = 'false' BEGIN TRANSACTION ELSE SAVE TRANSACTION SavepointName;
Betrachten Sie den Code in Teilen. Zuerst müssen wir feststellen, ob wir uns in einer Transaktion befinden:
DECLARE @isNestedTransaction BIT = CASE WHEN @@trancount > 0 THEN 'true' ELSE 'false' END;
Führen Sie nach dem Empfang des Flags @isNestedTransaction den TRY-Block aus und legen Sie je nach Situation den Sicherungspunkt oder den Start der Transaktion fest.
BEGIN TRY IF @isNestedTransaction = 'false' BEGIN TRANSACTION ELSE SAVE TRANSACTION SavepointName;
Nachdem wir etwas Nützliches getan haben, legen Sie fest, ob dies ein „echter“ Start des Verfahrens ist.
Wenn dies ein Start aus einem Testfall ist, müssen wir natürlich nichts festlegen. Am Ende der Ausführung setzt tSQLt einfach alle Änderungen zurück. Wenn plötzlich etwas schief gelaufen ist und wir in den CATCH-Block geraten sind, müssen wir zunächst herausfinden, ob unsere Transaktion überhaupt festgeschrieben werden kann.
BEGIN CATCH DECLARE @isCommitable BIT = CASE WHEN XACT_STATE() = 1 THEN 'true' ELSE 'false' END;
Wir können nur dann zum Sicherungspunkt zurückkehren, wenn
- festschreibbare Transaktion
- ein Testlauf findet statt, d.h. Sicherungspunkt vorhanden.
In anderen Fällen müssen wir die gesamte Transaktion zurücksetzen.
IF @isCommitable = 'true' AND @isNestedTransaction = 'true' ROLLBACK TRANSACTION SavepointName; ELSE ROLLBACK; THROW; END CATCH;
Ja, leider, wenn wir während eines Testlaufs eine nicht festschreibbare Transaktion erhalten haben, erhalten wir immer noch einen Fehler bei der Ausführung des Testfalls.
FakeTable und Problem mit dem Fremdschlüssel
Betrachten Sie die bekannten Tabellen [Studie] und [Klinik]:
Wir erinnern uns an die [TrialID] FK. Welche Probleme kann dies verursachen? In den zuvor angegebenen Beispielen haben wir FakeTable gleichzeitig auf beide Tabellen angewendet. Wenn wir es nur auf [Test] anwenden, erhalten wir die folgende Situation:
Der Versuch, auf diese Weise einen Eintrag in [Clinic] einzufügen, kann zum Fehlschlagen führen (selbst wenn wir alle erforderlichen Daten in der gefälschten Version der Tabelle [Trial] vorbereitet haben).
[dbo].[Test_FK_Problem] failed: (Error) The INSERT statement conflicted with the FOREIGN KEY constraint "Trial_Fk". The conflict occurred in database "HabrDemo", table "dbo.tSQLt_tempobject_ba8f36353f7a44f6a9176a7d1db02493", column 'TrialID'.[16,0]{Test_FK_Problem,14}
Fazit: Sie müssen entweder alles vortäuschen oder nichts vortäuschen. Im zweiten Fall ist es offensichtlich, dass die Basis im Voraus zum Testen vorbereitet werden muss.
SpyProcedure zu Systemprozeduren
Leider schlägt das Ausspionieren von Aufrufen von Systemprozeduren fehl.
[HabrDemo].[test_test] failed: (Error) Cannot use SpyProcedure on sys.sp_help because the procedure does not exist[16,10] {tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure,7}
Im Semaphor-Beispiel haben wir Aufrufe der von unseren Entwicklern geschriebenen [SendEmail] -Prozedur verfolgt. In diesem Fall muss beim Schreiben einer separaten Prozedur einige zusätzliche Informationen gesammelt und verarbeitet werden, bevor Nachrichten direkt gesendet werden. Im Allgemeinen muss man mental darauf vorbereitet sein, dass man für einige Systemprozeduren möglicherweise Interlayer-Prozeduren nur zum Testen schreiben muss.
Vorteile
Schnelle Installation
Die Installation erfolgt in 2 Schritten und dauert ca. 2 Minuten. Sie müssen nur die CLR auf dem Server aktivieren, falls dies noch nicht geschehen ist, und ein einzelnes Skript ausführen. Alles, Sie können die erste Testklasse hinzufügen und Testfälle schreiben.
Schnelle Entwicklung
tSQLt ist ein leicht zu erlernendes Tool. Ich habe einen kleinen Tag gebraucht, um es zu meistern. Ich fragte meine Kollegen, die mit dem Framework arbeiteten, und es stellte sich heraus, dass jeder ungefähr einen Tag verbringen würde.
Schnelle Implementierung in CI
Es dauerte ungefähr 2 Stunden, um die Integration in CI für unser Projekt einzurichten. Die Zeit kann natürlich variieren, aber im Allgemeinen ist dies kein Problem, und die Integration kann sehr schnell durchgeführt werden.
Große Auswahl an Werkzeugen
Dies ist eine subjektive Einschätzung, aber meiner Meinung nach ist die von tSQLt bereitgestellte Funktionalität ziemlich umfangreich und deckt den Löwenanteil der Bedürfnisse in der Praxis ab. In seltenen Fällen, in denen nicht genügend Fälschungen und Behauptungen eingebaut sind, gibt es natürlich tSQLt.Fail.
Bequeme Dokumentation
Die offizielle Dokumentation ist bequem und konsistent. Mit seiner Hilfe können Sie die Essenz der Verwendung von tSQLt in kurzer Zeit leicht verstehen, selbst wenn dies Ihr erstes Unit-Test-Tool ist.
Bequeme Ausgabe
Die Daten können in einer sehr klaren Textform erhalten werden:
[tSQLtDemo].[test_error_messages] failed: (Failure) Expected an error to be raised. [tSQLtDemo].[test_tables_comparison] failed: (Failure) useful and descriptive error message Unexpected/missing resultset rows! |_m_|Column1|Column2| +---+-------+-------+ |< |2 |Value2 | |= |1 |Value1 | |= |3 |Value3 | |> |2 |Value3 | +----------------------+ |Test Execution Summary| +----------------------+ |No|Test Case Name |Dur(ms)|Result | +--+------------------------------------+-------+-------+ |1 |[tSQLtDemo].[test_constraint] | 83|Success| |2 |[tSQLtDemo].[test_trial_view] | 83|Success| |3 |[tSQLtDemo].[test_error_messages] | 127|Failure| |4 |[tSQLtDemo].[test_tables_comparison]| 147|Failure| ----------------------------------------------------------------------------- Msg 50000, Level 16, State 10, Line 1 Test Case Summary: 4 test case(s) executed, 2 succeeded, 2 failed, 0 errored. -----------------------------------------------------------------------------
Sie können auch aus der Datenbank extrahieren (anklickbar) ...
... oder im XML-Format erhalten.
<?xml version="1.0" encoding="UTF-8"?> <testsuites> <testsuite id="1" name="tSQLtDemo" tests="3" errors="0" failures="1" timestamp="2019-06-22T16:46:06" time="0.433" hostname="BLAHBLAHBLAH\SQL2017" package="tSQLt"> <properties /> <testcase classname="tSQLtDemo" name="test_constraint" time="0.097" /> <testcase classname="tSQLtDemo" name="test_error_messages" time="0.153"> <failure message="Expected an error to be raised." type="tSQLt.Fail" /> </testcase> <testcase classname="tSQLtDemo" name="test_trial_view" time="0.156" /> <system-out /> <system-err /> </testsuite> </testsuites>
Mit der letzteren Option können Sie Tests einfach in CI integrieren. Insbesondere bei Atlassian Bamboo funktioniert bei uns alles.
Redgate-Support
Zu den Pluspunkten gehört die Unterstützung eines so großen Anbieters von DBA-Tools wie RedGate. SQL Test - das Plugin für SQL Server Management Studio - funktioniert sofort mit tSQLt. Darüber hinaus bietet RedGate dem tSQLt-Hauptentwickler Unterstützung bei der Entwicklungsumgebung, wie der Entwickler selbst bei
Google Groups behauptet.
Nachteile
Keine temporären Tischfälschungen
tSQLt erlaubt keine gefälschten temporären Tabellen. Bei Bedarf können Sie das
inoffizielle Add-On verwenden . Leider wird dieses Add-On nur von SQL Server 2016+ unterstützt.
Kein Zugriff auf externe Datenbanken
Es wird nicht funktionieren, eine separate Basis nur zum Speichern des Frameworks zu behalten. tSQLt wurde entwickelt, um zu testen, was in derselben Datenbank enthalten ist. Fake wird leider nicht funktionieren.
CREATE PROCEDURE [tSQLtDemo].[test_outer_db] AS BEGIN SELECT TOP 10 * FROM [AdventureWorks2017].[Person].[Password] EXEC tSQLt.FakeTable '[AdventureWorks2017].[Person].[Password]' SELECT TOP 10 * FROM [AdventureWorks2017].[Person].[Password] END
Behauptungen scheinen zu funktionieren, aber natürlich garantiert niemand ihre Leistung.
CREATE PROCEDURE [tSQLtDemo].[test_outer_db_assertions] AS BEGIN SELECT TOP 1 * INTO
Dokumentationsfehler
Trotz der Tatsache, dass ich oben über die Konsistenz und Zugänglichkeit der Dokumentation geschrieben habe, enthält sie auch Probleme. Es gibt einige veraltete Momente.
Beispiel 1. In der
Kurzanleitung wird vorgeschlagen , das Framework von SourceForge herunterzuladen. Sie verabschiedeten sich
2015 von SourceForge.
Beispiel 2. Das
ApplyConstraint-Handbuch im Beispiel verwendet eine Fail-Prozedur zum Abfangen einer Ausnahme, die durch ExpectException einfacher und visueller zu ersetzen wäre.
CREATE PROCEDURE ConstraintTests.[test ReferencingTable_ReferencedTable_FK prevents insert of orphaned rows] AS BEGIN EXEC tSQLt.FakeTable 'dbo.ReferencedTable'; EXEC tSQLt.FakeTable 'dbo.ReferencingTable'; EXEC tSQLt.ApplyConstraint 'dbo.ReferencingTable','ReferencingTable_ReferencedTable_FK'; DECLARE @ErrorMessage NVARCHAR(MAX); SET @ErrorMessage = ''; BEGIN TRY INSERT INTO dbo.ReferencingTable ( id, ReferencedTableId ) VALUES ( 1, 11 ) ; END TRY BEGIN CATCH SET @ErrorMessage = ERROR_MESSAGE(); END CATCH IF @ErrorMessage NOT LIKE '%ReferencingTable_ReferencedTable_FK%' BEGIN EXEC tSQLt.Fail 'Expected error message containing ''ReferencingTable_ReferencedTable_FK'' but got: ''',@ErrorMessage,'''!'; END END GO
Und das ist natürlich, weil es stattfindet ...
Teilweise Aufgabe
Die Entwicklung von tSQLt hat von Anfang 2016 bis Juni 2019 eine lange Pause eingelegt. Ja, leider wird dieses Tool teilweise aufgegeben. Nach
GitHub ging die Entwicklung 2019 nach und nach weiter voran. Obwohl die offiziellen Google Groups
einen Thread haben, in dem Sebastian, der Hauptentwickler von tSQLt, direkt nach dem Schicksal der Entwicklung gefragt wurde. Die letzte Frage wurde am 2. März 2019 gestellt, die Antwort ist noch nicht eingegangen.
Problem mit SQL Server 2017
Wenn Sie SQL Server 2017 verwenden, erfordert die Installation von tSQLt möglicherweise zusätzliche Manipulationen. , 2012 SQL Server security-. «CLR strict security», ( SAFE). (, ,
). .
, , « », tSQLt, , .
GitHub issue , , 2017 (. ).
(±)
. tSQLt . , (CLR, T-SQL SQL ), , . , tSQLt , SQL-powered .
, PostgreSQL
ptTAP . «» PL/pgSQL TAP. MySQL , —
MyTAP . Oracle,
utPLSQL — ( , ) .
Fazit
, .
— . SQL Server, Oracle MySQL — . , . , , , , , .
— . , , SQL Server, tSQLt 100% , , . , , .