Entwerfen und Implementieren einer Teststrategie

Abgeschlossen

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:

  1. Öffnen Sie Ihr SQL-Datenbankprojekt.
  2. Suchen Sie im SQL Server-Objekt-Explorer die gespeicherte Prozedur oder Funktion, die Sie testen möchten.
  3. Klicken Sie mit der rechten Maustaste auf das Objekt, und wählen Sie "Komponententests erstellen" aus.
  4. Wählen Oder erstellen Sie ein C#-Testprojekt.
  5. Legen Sie die Testverbindung mit Ihrer Entwicklungsdatenbank fest.
  6. 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.