Anmerkung
Der Zugriff auf diese Seite erfordert eine Genehmigung. Du kannst versuchen, dich anzumelden oder die Verzeichnisse zu wechseln.
Der Zugriff auf diese Seite erfordert eine Genehmigung. Du kannst versuchen , die Verzeichnisse zu wechseln.
MSTest bietet einen gut definierten Lebenszyklus für Testklassen und Testmethoden, sodass Sie Setup- und Teardownvorgänge in verschiedenen Phasen der Testausführung durchführen können. Wenn Sie den Lebenszyklus verstehen, können Sie effiziente Tests schreiben und häufige Fallstricke vermeiden.
Übersicht über den Lebenszyklus
Der Lebenszyklus gliedert sich in vier Phasen, die von der höchsten Ebene (Assembly) bis zur niedrigsten Ebene (Testmethode) ausgeführt werden.
- Assemblyebene: Wird einmal ausgeführt, wenn die Testassembly geladen und entladen wird
- Klassenebene: Wird einmal pro Testklasse ausgeführt
- Globale Testebene: Wird vor und nach jeder Testmethode in der Assembly ausgeführt
- Testebene: Wird für jede Testmethode ausgeführt (einschließlich jeder Datenzeile in parametrisierten Tests)
Lebenszyklus auf Assemblyebene
Assembly-Lebenszyklusmethoden werden einmal ausgeführt, wenn die Testassembly geladen und entladen wird. Verwenden Sie diese für teure einmalige Einrichtung, z. B. datenbankinitialisierung oder Dienststart.
AssemblyInitialize und AssemblyCleanup
[TestClass]
public class AssemblyLifecycleExample
{
private static IHost? _host;
[AssemblyInitialize]
public static async Task AssemblyInit(TestContext context)
{
// Runs once before any tests in the assembly
_host = await StartTestServerAsync();
context.WriteLine("Test server started");
}
[AssemblyCleanup]
public static async Task AssemblyCleanup(TestContext context)
{
// Runs once after all tests complete
// TestContext parameter available in MSTest 3.8+
if (_host != null)
{
await _host.StopAsync();
}
}
private static Task<IHost> StartTestServerAsync()
{
// Server initialization
return Task.FromResult<IHost>(null!);
}
}
Anforderungen
- Methoden müssen
public static - Rückgabetyp:
void, ,TaskoderValueTask(MSTest v3.3+) -
AssemblyInitializeerfordert einenTestContextParameter -
AssemblyCleanupakzeptiert Nullparameter oder einenTestContextParameter (MSTest 3.8+) - Pro Baugruppe ist nur ein Attribut zulässig.
- Muss in einer Klasse vorhanden sein, die mit
[TestClass]markiert ist.
Tipp
Verwandte Analysegeräte:
-
MSTEST0012 – überprüft
AssemblyInitializedie Signatur. -
MSTEST0013 – überprüft
AssemblyCleanupdie Signatur.
Lebenszyklus auf Klassenebene
Klassenlebenszyklusmethoden werden einmal pro Testklasse ausgeführt, bevor und nach allen Testmethoden in dieser Klasse. Verwenden Sie diese für das Setup, das für Tests in einer Klasse geteilt wird.
ClassInitialize und ClassCleanup
[TestClass]
public class ClassLifecycleExample
{
private static HttpClient? _client;
[ClassInitialize]
public static void ClassInit(TestContext context)
{
// Runs once before any tests in this class
_client = new HttpClient
{
BaseAddress = new Uri("https://api.example.com")
};
}
[ClassCleanup]
public static void ClassCleanup()
{
// Runs after all tests in this class complete
_client?.Dispose();
}
[TestMethod]
public async Task GetUsers_ReturnsSuccess()
{
var response = await _client!.GetAsync("/users");
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
Anforderungen
- Methoden müssen
public static - Rückgabetyp:
void, ,TaskoderValueTask(MSTest v3.3+) -
ClassInitializeerfordert einenTestContextParameter -
ClassCleanupakzeptiert Nullparameter oder einenTestContextParameter (MSTest 3.8+) - Pro Klasse ist nur ein Attribut zulässig.
Vererbungsverhalten
Steuern, ob ClassInitialize für abgeleitete Klassen mithilfe von InheritanceBehavior:
[TestClass]
public class BaseTestClass
{
[ClassInitialize(InheritanceBehavior.BeforeEachDerivedClass)]
public static void BaseClassInit(TestContext context)
{
// Runs before each derived class's tests
}
}
[TestClass]
public class DerivedTestClass : BaseTestClass
{
[TestMethod]
public void DerivedTest()
{
// BaseClassInit runs before this class's tests
}
}
| Vererbungsverhalten | Description |
|---|---|
None (Standardwert) |
Initialisieren wird nur für die deklarierende Klasse ausgeführt |
BeforeEachDerivedClass |
Initialisieren von Läufen vor jeder abgeleiteten Klasse |
Tipp
Verwandte Analysegeräte:
-
MSTEST0010 – überprüft
ClassInitializedie Signatur. -
MSTEST0011 – überprüft
ClassCleanupdie Signatur. -
MSTEST0034 - empfiehlt die Verwendung
ClassCleanupBehavior.EndOfClass.
Globaler Lebenszyklus auf Testebene
Hinweis
Globale Testlebenszyklusattribute wurden in MSTest 3.10.0 eingeführt.
Globale Testlebenszyklusmethoden werden vor und nach jeder Testmethode in der gesamten Assembly ausgeführt, ohne dass jeder Testklasse Code hinzugefügt werden muss.
GlobalTestInitialize und GlobalTestCleanup
[TestClass]
public class GlobalTestLifecycleExample
{
[GlobalTestInitialize]
public static void GlobalTestInit(TestContext context)
{
// Runs before every test method in the assembly
context.WriteLine($"Starting test: {context.TestName}");
}
[GlobalTestCleanup]
public static void GlobalTestCleanup(TestContext context)
{
// Runs after every test method in the assembly
context.WriteLine($"Finished test: {context.TestName}");
}
}
Anforderungen
- Methoden müssen
public static - Rückgabetyp:
void, ,TaskoderValueTask - Muss genau einen
TestContextParameter aufweisen - Muss in einer mit
[TestClass]gekennzeichneten Klasse sein - Für das Assembly sind mehrere Methoden mit diesen Attributen zulässig.
Hinweis
Wenn mehrere Methoden GlobalTestInitialize oder GlobalTestCleanup vorhanden sind, wird die Ausführungsreihenfolge nicht garantiert. Dies TimeoutAttribute wird für GlobalTestInitialize Methoden nicht unterstützt.
Tipp
Verwandter Analysator: MSTEST0050 - überprüft globale Prüfgerätemethoden.
Lebenszyklus der Teststufe
Der Lebenszyklus wird auf Testebene für jede Testmethode ausgeführt. Bei parametrisierten Tests wird der Lebenszyklus für jede Datenzeile ausgeführt.
Einrichtungsphase
Verwenden Sie TestInitialize oder einen Konstruktor zur Einrichtung für jeden Test:
[TestClass]
public class TestLevelSetupExample
{
private Calculator? _calculator;
public TestLevelSetupExample()
{
// Constructor runs before TestInitialize
// Use for simple synchronous initialization
}
[TestInitialize]
public async Task TestInit()
{
// Runs before each test method
// Supports async, attributes like Timeout
_calculator = new Calculator();
await _calculator.InitializeAsync();
}
[TestMethod]
public void Add_TwoNumbers_ReturnsSum()
{
var result = _calculator!.Add(2, 3);
Assert.AreEqual(5, result);
}
}
Konstruktor vs. TestInitialize:
| Aspekt | Konstruktor | TestInitialize |
|---|---|---|
| Asynchrone Unterstützung | Nein | Yes |
| Timeoutunterstützung | Nein | Ja (mit [Timeout] Attribut) |
| Ausführungsreihenfolge | First (Erster) | Nach Konstruktor |
| Vererbung | Basis dann abgeleitet | Basis dann abgeleitet |
| Ausnahmeverhalten | Bereinigung und Dispose werden nicht ausgeführt (es ist keine Instanz vorhanden) | Bereinigen und Entsorgen werden weiterhin ausgeführt |
Tipp
Welchen Ansatz sollte ich verwenden? Konstruktoren werden im Allgemeinen bevorzugt, da sie die Verwendung von readonly Feldern ermöglichen, was die Unveränderlichkeit erzwingt und es einfacher macht, die Testklasse nachzuvollziehen. Verwenden Sie diese Methode TestInitialize , wenn Sie asynchrone Initialisierungs- oder Timeoutunterstützung benötigen.
Sie können auch beide Ansätze kombinieren: Verwenden Sie den Konstruktor für die einfache synchrone Initialisierung von readonly Feldern und TestInitialize für zusätzliche asynchrone Einrichtung, die von diesen Feldern abhängt.
Sie können optional Codeanalysatoren aktivieren, um einen konsistenten Ansatz zu erzwingen:
- MSTEST0019 - TestInitialize-Methoden gegenüber Konstruktoren bevorzugen
- MSTEST0020 – Bevorzugen von Konstruktoren gegenüber TestInitialize-Methoden
Ausführungsphase
Die Testmethode wird nach Abschluss des Setups ausgeführt. Für async Testmethoden wartet MSTest auf die zurückgegebene Task oder ValueTask.
Warnung
Asynchrone Testmethoden verfügen nicht standardmäßig über eine SynchronizationContext . Dies gilt nicht für UITestMethod Tests in UWP und WinUI, die im UI-Thread ausgeführt werden.
Bereinigungsphase
Verwenden TestCleanup oder IDisposable/IAsyncDisposable für die Bereinigung pro Test:
[TestClass]
public class TestLevelCleanupExample
{
private HttpClient? _client;
[TestInitialize]
public void TestInit()
{
_client = new HttpClient();
}
[TestCleanup]
public void TestCleanup()
{
if (_client != null)
{
_client.Dispose();
}
}
[TestMethod]
public async Task GetData_ReturnsSuccess()
{
var response = await _client!.GetAsync("https://example.com");
Assert.IsTrue(response.IsSuccessStatusCode);
}
}
Bereinigungsausführungsreihenfolge (abgeleitet zur Basis):
-
TestCleanup(abgeleitete Klasse) -
TestCleanup(Basisklasse) -
DisposeAsync(falls implementiert) -
Dispose(falls implementiert)
Tipp
Sie können Code-Analysewerkzeuge aktivieren, um eine konsistente Bereinigungsstrategie zu erzwingen.
- MSTEST0021 – Die Verwendung von Dispose-Methoden gegenüber TestCleanup-Methoden bevorzugen
- MSTEST0022 - TestCleanup über Dispose-Methoden bevorzugen
Wenn Sie nicht-MSTest-Analysetools aktiviert haben, z. B. .NET-Codeanalyseregeln, wird möglicherweise CA1001 vorgeschlagen, das Dispose-Muster zu implementieren, wenn Ihre Testklasse freigebbare Ressourcen besitzt. Dies ist ein erwartetes Verhalten, und Sie sollten die Anleitungen des Analyzers befolgen.
Vollständige Reihenfolge auf Testebene
- Erstellen einer Instanz der Testklasse (Konstruktor)
- Eigenschaft
TestContextfestlegen (falls vorhanden) - Ausführen von
GlobalTestInitializeMethoden - Führe
TestInitialize-Methoden (von Basis zu abgeleitet) aus - Testmethode ausführen
- Aktualisieren Sie
TestContextmit Ergebnissen (z. B.Outcome-Eigenschaft) - Ausführen von
TestCleanupMethoden (abgeleitet auf Basis) - Ausführen von
GlobalTestCleanupMethoden -
DisposeAsyncausführen (falls implementiert) - Ausführen
Dispose(falls implementiert)
Tipp
Verwandte Analysegeräte:
-
MSTEST0008 – überprüft
TestInitializedie Signatur. -
MSTEST0009 – überprüft
TestCleanupdie Signatur. - MSTEST0063 – überprüft den Testklassenkonstruktor.
Bewährte Methoden
Verwenden Sie den geeigneten Bereich: Richten Sie die Konfiguration auf der sinnvoll höchsten Ebene ein, um redundante Arbeit zu vermeiden.
Halten Sie die Einrichtung schnell: Eine langwierige Einrichtung wirkt sich auf alle Tests aus. Erwägen Sie die faule Initialisierung für teure Ressourcen.
Sorgfältig bereinigen: Entfernen Sie immer Ressourcen, um Teststörungen und Speicherverluste zu verhindern.
Behandeln Sie asynchrone Vorgänge ordnungsgemäß: Verwenden Sie
async TaskRückgabetypen, nichtasync void, für asynchrone Lebenszyklus-Methoden.Erwägen Sie die Testisolation: Jeder Test sollte unabhängig sein. Vermeiden Sie gemeinsamen änderbaren Zustand zwischen Tests.
Verwenden Sie GlobalTest sparsam: Globale Lebenszyklusmethoden werden für jeden Test ausgeführt, daher sollten sie ressourcenschonend bleiben.