Als Teil unserer Testsuite haben wir neben den Unit-Tests, die alles simulieren und keine Datenbankverbindung erfordern, auch Integrationstests, die eine Datenbank erfordern.
Die Integrationstests sind erforderlich, da wir mit viel Legacy-Code arbeiten und sie uns die Möglichkeit bieten, Tests auf hoher Ebene durchzuführen.
DIE EINRICHTUNG
Die Datenbank ist ein SQL Server 2008 R2, der auf einem Windows Server 2008 R2-System mit allen neuesten Windows-Updates ausgeführt wird. Sowohl für das Betriebssystem als auch für den SQL Server.
Die VM, auf der der Datenbankserver läuft, ist Teil unserer Build-Infrastruktur und wird jeden Morgen um 6 Uhr neu erstellt, natürlich basierend auf einem Image, und um 22 Uhr gelöscht. Ich weiß also, dass der SQL Server-Agent und -Dienst im Wesentlichenneuund jeden Tag gestartet. Der erste Build erfolgt um 7 Uhr morgens, sodass die Maschine genügend Zeit hat, alle Dienste zu starten und zu laden.
Der Datenbankserver ist so konfiguriert, dass eine unbegrenzte Zahl von Verbindungen zulässig ist und Named Pipes und TCP-Verbindungen aktiviert sind.
Die Verbindung zur Datenbank erfolgt über densBenutzer.
Wir haben einen abgespeckten Snapshot unserer Produktionsdatenbank,ein.mdf, das alle Tabellen, Ansichten, gespeicherten Prozeduren und einen Mindestdatensatz enthält, der zum Durchführen der Tests erforderlich ist.
Wenn ein Integrationstest ausgeführt wird, kopiert das Test-Setupein.mdfin den DATA-Ordner unserer SQL Server-Installation alsb.mdf. b.mdf wird dann mit dem folgenden Befehl an die Datenbank angehängt:
CREATE DATABASE Foo ON (FILENAME = N'Path\To\b.mdf') FOR ATTACH
Es werden Tests ausgeführt, Datenbankoperationen werden durchgeführt und beim Testabbau der Testvorrichtung wird die Datenbank getrennt und die Datei b.mdf entfernt.
Zum Trennen werden die folgenden beiden Befehle einzeln ausgeführt:
ALTER DATABASE Foo SET SINGLE_USER WITH ROLLBACK IMMEDIATE
EXEC master.dbo.sp_detach_db @dbname = N'Foo'
In der Praxis habe ich also einen Satz Testvorrichtungen mit dem folgenden Layout:
Setup();
Test_1();
Test_2();
Test_3();
TearDown();
Jedes Setup erstellt eine neue Datenbank, führt alle Tests aus und entfernt die Datenbank, sodass die nächste Textvorrichtung mit einer sauberen, neuen Datenbank beginnt.
Insgesamt habe ich etwa 50 Text-Fixtures, die jeweils 10 Tests enthalten. Das bedeutet, dass eine Datenbank 50 Mal angehängt und abgetrennt wird und etwa 500 Tests ausgeführt werden.
DAS PROBLEM
In den letzten Wochen habe ich einen Anstieg der Anzahl fehlgeschlagener Builds im Zusammenhang mit den Integrationstests festgestellt. Ich weiß, dass meine Tests in Ordnung sind, da das gesamte Setup auf meinem lokalen Computer und auf den Computern der anderen Entwickler einwandfrei funktioniert. Nur der Build-Server meldet ein Problem:
SetUp Error : Namespace.Class.Method
SetUp : System.Data.SqlClient.SqlException : Cannot open database "Foo" requested by the login. The login failed.
Login failed for user 'sa'.
Natürlich habe ich gegoogelt und ja, die Anmeldung ist korrekt. Ich weiß das, weil nicht immer derselbe Test fehlschlägt. Wenn ich die gesamte Testsuite 10 Mal ausführe, schlägt sie in 8 von 10 Fällen fehl, aber der Test, der einen Fehler meldet, ist jedes Mal anders. Die Fehlermeldung ist dieselbe, nämlich, dass die Anmeldung nicht möglich ist, und manchmal wird auch gemeldet, dassEs gibt keinen Prozess am anderen Ende der Leitung.
Ich habe außerdem überprüft, ob Named Pipe- und TCP-Verbindungen aktiviert sind, ich habe die Anzahl der zulässigen Verbindungen überprüft, ... Ich habe die ERRORLOG-Datei überprüft, aber sie enthält nichts, was direkt mit meiner Datenbank zusammenhängt.
Ich vermute, dass es aus irgendeinem Grund zu schnell oder zu langsam ist und die Datenbank nicht richtig verbinden oder trennen kann, oder dass der SINGLE_USER
Aufruf das Problem verursacht. Soweit ich weiß, kann die Datei b.mdf nicht entfernt werden, wenn ein Test aufgrund der Anmeldung fehlschlägt, da die Datei anscheinend verwendet wird.
Meine Frage ist also: Gibt es noch etwas, das ich versuchen kann? Gibt es eine Fehlerprotokolldatei oder eine bestimmte Meldung, die mir weitere Einblicke geben kann? Kann ich etwas tun, um zu überprüfen, ob das Anhängen und Trennen erfolgreich waren? (Ist es möglich, dass ein fehlgeschlagenes Trennen das Anmeldeproblem verursacht?) Ist der Trennvorgang asynchron und ist es daher möglich, dass er noch nicht abgeschlossen ist, wenn der nächste Anruf erfolgt?
Antwort1
Erstes Problem: Fehler beim Anmelden.
Ihre Datenbank ist höchstwahrscheinlich noch nicht vollständig initialisiert, wenn die Tests ausgeführt werden.
Sie sollten dies in Ihrem Verfahren erkennen. Eine einfache Möglichkeit hierfür besteht darin, die Masterdatenbank abzufragen, um festzustellen, ob die Zieldatenbank betriebsbereit ist.
IF (select name from sys.databases
where name = 'foo' and state_desc = 'ONLINE' and is_in_standby = '0') IS NOT NULL
PRINT 'database not found';
Zweites Problem: kein Prozess am anderen Ende der Leitung.
Der eigentliche Fehler wird oft verdeckt, wenn Sie keine Verbindung über TCP/IP herstellen.
Sie können versuchen, direkte IP-Verbindungen zu aktivieren, oder Sie können sich auf die anderen Fehler konzentrieren. Diese sind wahrscheinlich die Ursache für diesen Fehler.