Entwerfen und Implementieren einer Teststrategie
Ein SQL-Datenbankprojekt, das erfolgreich erstellt wird, bedeutet nicht, dass die gespeicherten Prozeduren darin die richtigen Ergebnisse zurückgeben. Eine Prozedur kann ohne Fehler kompiliert werden und trotzdem die falschen Summen berechnen oder eine Überprüfungsprüfung überspringen. Das Testen fängt diese Probleme auf, bevor sie die Produktion erreichen.
Grundlegendes zu Datenbanktestebenen
Datenbanktests funktionieren auf Ebenen, wobei jeweils eine andere Problemklasse abfangen wird:
-
Buildüberprüfung ist der
dotnet build-Schritt, der bereits vorhanden ist. Es erfasst Syntaxfehler und fehlerhafte Objektverweise. Schnell, aber es beweist nur, dass das Schema strukturell gültig ist. - Komponententests überprüfen, ob einzelne gespeicherte Prozeduren und Funktionen die richtigen Ergebnisse für einen bestimmten Satz von Eingaben liefern. Sie erfassen Logikfehler.
- Integrationstests üben End-to-End-Szenarien mit einer bereitgestellten Datenbank aus. Sie beweisen, dass Objekte korrekt zusammenarbeiten, erfordern jedoch eine laufende Instanz und nehmen die meiste Zeit in Anspruch.
Jede Ebene ist langsamer und teurer als die zuvor, fängt aber auch Probleme auf, die die vorherige Ebene nicht fangen kann.
Erstellen von SQL Server-Komponententests
SQL Server Data Tools (SSDT) in Visual Studio enthält ein integriertes Framework für Datenbankeinheitstests. Jeder Test führt T-SQL anhand einer Livedatenbank aus und überprüft die Ergebnisse mithilfe von Testbedingungen.
Struktur eines Komponententests
Jeder Test verfügt über drei Abschnitte, die einem Setup-Execute-Cleanup-Muster folgen:
- Vorabtest: Richten Sie die Daten ein, die die Testanforderungen erfüllen. Fügen Sie Kundendatensätze ein, löschen Sie Daten aus vorherigen Durchläufen.
- Test: Führen Sie den Testvorgang aus. Rufen Sie die gespeicherte Prozedur auf, und fragen Sie die Ansicht ab.
- Nach dem Test: Führen Sie eine Bereinigung durch, damit der Test die nächste Ausführung nicht kontaminiert.
Testbedingungen
Nachdem der Test T-SQL ausgeführt wurde, überprüfen die Testbedingungen, was zurückkam. Die am häufigsten verwendeten Bedingungen sind:
- Zeilenanzahl: Überprüft, ob das Resultset die erwartete Anzahl von Zeilen enthält.
- Skalarwert: Überprüft, ob eine bestimmte Zelle im Resultset den erwarteten Wert enthält.
- Erwartetes Schema: Überprüft, ob das Resultset über die erwarteten Spaltennamen und Datentypen verfügt.
Andere integrierte Bedingungen umfassen Datenprüfsumme, Leere Ergebnismenge, Nicht leere Ergebnismenge und Ausführungszeit.
Erstellen eines Komponententestprojekts
Das Einrichten von SQL Server-Komponententests in Visual Studio erfordert einige Schritte:
- Öffnen Sie Ihr SQL-Datenbankprojekt.
- Suchen Sie im SQL Server-Objekt-Explorer die gespeicherte Prozedur oder Funktion, die Sie testen möchten.
- Klicken Sie mit der rechten Maustaste auf das Objekt, und wählen Sie "Komponententests erstellen" aus.
- Wählen Oder erstellen Sie ein C#-Testprojekt.
- Legen Sie die Testverbindung mit Ihrer Entwicklungsdatenbank fest.
- Wählen Sie "Datenbankprojekt automatisch bereitstellen" aus, bevor Komponententests ausgeführt werden. Mit dieser Option wird die Testdatenbank mit den neuesten Projektänderungen synchronisiert.
Der Designer wird mit einer T-SQL-Vorlage geöffnet, in der Sie Ihre Testlogik schreiben und Testbedingungen anfügen.
Schreiben effektiver Datenbankeinheitstests
Ein guter Komponententest beantwortet eine bestimmte Frage: "Macht dieses Verfahren, was es für eine bekannte Eingabe tun soll?" Im Folgenden finden Sie ein Beispiel für Tests uspPlaceNewOrder, bei denen überprüft wird, ob das Aufgeben einer Bestellung die Jahres-bis-Datum-Summe des Kunden ordnungsgemäß aktualisiert:
-- Pre-test: Set up customer and clear previous data
DECLARE @CustomerID INT;
INSERT INTO [Sales].[Customer] (CustomerName) VALUES (N'Test Customer');
SET @CustomerID = SCOPE_IDENTITY();
DELETE FROM [Sales].[Orders] WHERE [CustomerID] = @CustomerID;
-- Test: Place an order and verify YTDOrders updated correctly
DECLARE @RC INT;
EXECUTE @RC = [Sales].[uspPlaceNewOrder] @CustomerID, 100, GETDATE(), 'O';
SELECT [YTDOrders]
FROM [Sales].[Customer]
WHERE [CustomerID] = @CustomerID;
📝 Koppeln Sie diesen T-SQL-Test mit einer Skalarwert-Testbedingung , die erwartet, dass der YTDOrders Wert sein wird 100.
Negative Tests
Außerdem müssen Sie überprüfen, ob gespeicherte Prozeduren ordnungsgemäß fehlschlagen, wenn sie ungültige Eingaben erhalten. Beispielsweise sollte das Stornieren einer bereits ausgelieferten Bestellung einen Fehler auslösen, nicht unbemerkt erfolgreich sein. Ein negativer Test bestätigt, dass der erwartete Fehler auftritt.
Wenn Sie in Schritt 3 des vorherigen Abschnitts " Komponententests erstellen " auswählen, generiert Visual Studio im Projektmappen-Explorer ein C#-Testprojekt mit einer .cs Datei, die einen Test für jede ausgewählte gespeicherte Prozedur enthält. Wenn Sie beispielsweise einen Test erstellt habenuspCancelOrder, enthält die generierte Datei einen Abschnitt mit dem Namen Sales_uspCancelOrder_Test. Um diesen Test als negativen Test zu markieren, öffnen Sie die .cs Datei im Projektmappen-Explorer , und fügen Sie das ExpectedSqlException Attribut direkt darüber hinzu, und speichern Sie die Datei:
[TestMethod()]
[ExpectedSqlException(Severity = 16, MatchFirstError = false, State = 1)]
public void Sales_uspCancelOrder_FilledOrder_Test()
Der Test besteht nur, wenn die gespeicherte Prozedur einen Fehler auslöst, der dem angegebenen Schweregrad und Zustand entspricht. Wenn das Verfahren geräuschlos erfolgreich ist, schlägt der Test fehl, was genau das gewünschte Ergebnis ist.
Entwerfen eines Integrationstestansatzes
Komponententests isolieren einzelne Objekte. Integrationstests gehen weiter und testen Szenarien, die mehrere Vorgänge umfassen. Sie beantworten Fragen wie:
- Belässt eine Abfolge gespeicherter Prozeduraufrufe die Datenbank im erwarteten Zustand?
- Werden Trigger richtig ausgelöst, wenn Daten über die Anwendungsschicht eintreffen?
- Geben Ansichten nach einer Reihe verknüpfter Tabellenänderungen korrekte Ergebnisse zurück?
Integrationstests benötigen eine dedizierte Datenbank. Stellen Sie das SQL-Datenbankprojekt in einer Testinstanz bereit, bevor jede Ausführung beginnt, indem Sie die Einstellung Datenbankprojekt automatisch bereitstellen, bevor Komponententests durchgeführt werden verwenden. Diese Einstellung hält das Testschema aktuell.
Überlegungen zu Testdatenbanken
Sowohl Komponententests als auch Integrationstests führen T-SQL für eine Livedatenbank aus, sodass die Testumgebung sorgfältig eingerichtet werden muss, um zuverlässige Ergebnisse zu erzielen.
- Isolieren Sie Tests von Produktionssystemen. Verwenden Sie eine separate Instanz oder dedizierte Testdatenbank. Das Ausführen von Tests für die Produktion wird niemals empfohlen.
- Setzen Sie vor jeder Ausführung auf einen bekannten Zustand zurück. Skripts nach der Bereitstellung oder Testbereinigungsskripts behandeln dieses Problem.
- Externalisieren Sie Verbindungszeichenfolgen in Konfigurationsdateien, sodass lokale Entwicklungs- und CI-Pipelines jeweils ohne Codeänderungen auf die richtige Datenbank verweisen.
Tests in CI/CD-Pipelines integrieren
Fügen Sie nach dem Build und der Bereitstellung einen Testschritt hinzu, damit Tests bei jedem Commit automatisch ausgeführt werden. Verwenden Sie in Azure DevOps die VSTest Aufgabe:
- task: VSTest@2
inputs:
testAssembly: '**\*Tests.dll'
searchFolder: '$(System.DefaultWorkingDirectory)'
Führen Sie in GitHub-Aktionen die Tests mithilfe von dotnet test:
- name: Run database unit tests
run: dotnet test ./DatabaseTests/DatabaseTests.csproj
Tipp
Konfigurieren Sie das Testprojekt so, dass das Datenbankprojekt vor dem Ausführen von Tests automatisch bereitgestellt wird. Diese Einstellung stellt sicher, dass das Testdatenbankschema zum Zeitpunkt des Tests mit dem Projekt übereinstimmt.
Wenn ein Test fehlschlägt, stoppt die Pipeline, und die Änderung erreicht weder die Staging- noch die Produktionsumgebung. Diese Verzögerung gibt dem Team Zeit, um das Problem zu beheben, bevor es sich auf eine Umgebung auswirkt.
Wichtige Erkenntnisse
Datenbanktests umfassen drei Ebenen: Build-Validierung für strukturelle Korrektheit, Unit-Tests für Logikverifizierung und Integrationstests für End-to-End-Workflows. SQL Server-Komponententests folgen einer dreistufigen Struktur von Testvoraktionen, Testaktionen und Tests nach Aktionen. Um das Verhalten gespeicherter Prozeduren und Funktionen zu überprüfen, verwenden Sie Testbedingungen wie Scalar Value, Row Countund Expected Schema. Um Methoden zu testen, die Fehlerbehandlungspfade überprüfen sollten, fügen Sie das ExpectedSqlException Attribut hinzu. Binden Sie Testprojekte in Ihre CI/CD-Pipeline ein, sodass durch einen fehlgeschlagenen Test die Bereitstellung blockiert wird, bevor Änderungen das Staging oder die Produktion erreichen. Zusammen bilden diese drei Ebenen ein Sicherheitsnetz, mit dem Ihr Team Datenbankänderungen zuversichtlich (und nicht besorgt) bereitstellen kann.