Dela via


MSTest-livscykel

MSTest tillhandahåller en väldefinierad livscykel för testklasser och testmetoder, så att du kan utföra konfigurations- och nedrullningsåtgärder i olika skeden av testkörningen. Genom att förstå livscykeln kan du skriva effektiva tester och undvika vanliga fallgropar.

Livscykelöversikt

Livscykeln grupperas i fyra steg, som körs från högsta nivå (sammansättning) till lägsta nivå (testmetod):

  1. Sammansättningsnivå: Körs en gång när testsammansättningen läses in och tas bort
  2. Klassnivå: Körs en gång per testklass
  3. Global testnivå: Körs före och efter varje testmetod i sammansättningen
  4. Testnivå: Körs för varje testmetod (inklusive varje datarad i parametriserade tester)

Livscykel på sammansättningsnivå

Metoder för assembly-livscykel körs en gång när testassemblyn läser in och avläser. Använd dessa för dyra engångskonfigurationer som databasinitiering eller start av tjänsten.

AssemblyInitialize och 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!);
    }
}

Kravspecifikation

  • Metoderna måste vara public static
  • Returtyp: void, Taskeller ValueTask (MSTest v3.3+)
  • AssemblyInitialize kräver en TestContext parameter
  • AssemblyCleanup accepterar noll parametrar eller en TestContext parameter (MSTest 3.8+)
  • Endast ett av varje attribut tillåts per sammansättning
  • Måste finnas i en klass som är markerad med [TestClass]

Tips/Råd

Relaterade analysverktyg:

  • MSTEST0012 – verifierar AssemblyInitialize signaturen.
  • MSTEST0013 – verifierar AssemblyCleanup signaturen.

Livscykel på klassnivå

Klasslivscykelmetoder körs en gång per testklass, före och efter alla testmetoder i den klassen. Använd dessa för konfiguration som delas mellan tester i en klass.

ClassInitialize och 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);
    }
}

Kravspecifikation

  • Metoderna måste vara public static
  • Returtyp: void, Taskeller ValueTask (MSTest v3.3+)
  • ClassInitialize kräver en TestContext parameter
  • ClassCleanup accepterar noll parametrar eller en TestContext parameter (MSTest 3.8+)
  • Endast ett av varje attribut tillåts per klass

Arvsbeteende

Kontrollera om ClassInitialize körs för härledda klasser med hjälp av 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
    }
}
InheritanceBehavior Description
None (standardinställning) Initiera endast körningar för deklareringsklassen
BeforeEachDerivedClass Initiera exekveringar före varje härledd klass

Tips/Råd

Relaterade analysverktyg:

  • MSTEST0010 – verifierar ClassInitialize signaturen.
  • MSTEST0011 – verifierar ClassCleanup signaturen.
  • MSTEST0034 – rekommenderar att du använder ClassCleanupBehavior.EndOfClass.

Global livscykel på testnivå

Anmärkning

Globala livscykelattribut för test introducerades i MSTest 3.10.0.

Globala testlivscykelmetoder körs före och efter varje testmetod i hela sammansättningen, utan att behöva lägga till kod i varje testklass.

GlobalTestInitialize och 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}");
    }
}

Kravspecifikation

  • Metoderna måste vara public static
  • Returtyp: void, Taskeller ValueTask
  • Måste ha exakt en TestContext parameter
  • Måste finnas i en klass som är markerad med [TestClass]
  • Flera metoder med dessa attribut tillåts i hela sammansättningen

Anmärkning

När det finns flera metoder av typen GlobalTestInitialize eller GlobalTestCleanup, garanteras inte körningsordningen. TimeoutAttribute Stöds inte på GlobalTestInitialize metoder.

Tips/Råd

Relaterat analysverktyg: MSTEST0050 – validerar globala testfixturmetoder.

Livscykel på testnivå

Livscykel på testnivå körs för varje testmetod. För parametriserade tester körs livscykeln för varje datarad.

Installationsfas

Använd TestInitialize eller en konstruktor för konfiguration per 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 jämfört med TestInitialize:

Aspekt Konstruktor TestInitialize
Async-stöd Nej Yes
Stöd för timeout Nej Ja (med [Timeout] attribut)
Körningsordning Först Efter konstruktor
Arv Bas som sedan härleds Bas som sedan härleds
Undantagsbeteende Cleanup och Dispose körs inte (det finns ingen instans) Rensning och bortskaffning körs fortfarande

Tips/Råd

Vilken metod ska jag använda? Konstruktorer är vanligtvis att föredra eftersom de gör att du kan använda fälten readonly, vilket säkerställer oföränderlighet och gör din testklass lättare att förstå. Använd TestInitialize när du behöver asynkron initiering eller timeout-stöd.

Du kan också kombinera båda metoderna: använd konstruktorn för enkel synkron initiering av fält och readonly för ytterligare asynkron konfiguration som är beroende av TestInitialize dessa fält.

Du kan också aktivera kodanalyserare för att framtvinga en konsekvent metod:

  • MSTEST0019 – Föredrar TestInitialisera metoder framför konstruktorer
  • MSTEST0020 – Föredrar konstruktorer framför TestInitialize-metoder

Utförandefas

Testmetoden körs när installationen har slutförts. För async testmetoder väntar MSTest på den returnerade Task eller ValueTask.

Varning

Asynkrona testmetoder har inte någon SynchronizationContext som standard. Detta gäller inte för UITestMethod tester i UWP och WinUI, som körs på användargränssnittstråden.

Klareringsfas

Använd TestCleanup eller IDisposable/IAsyncDisposable för rensning per 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);
    }
}

Körningsordning för rensning (härledd till bas):

  1. TestCleanup (härledd klass)
  2. TestCleanup (basklass)
  3. DisposeAsync (om det implementeras)
  4. Dispose (om det implementeras)

Tips/Råd

Du kan också aktivera kodanalyserare för att tillämpa en konsekvent rensningsmetod:

  • MSTEST0021 – Föredra Dispose framför TestCleanup-metoder
  • MSTEST0022 – Föredrar TestCleanup framför borttagningsmetoder

Om du har icke-MSTest-analysverktyg aktiverade, till exempel .NET-kodanalysregler, kan CA1001 föreslå att du implementerar borttagningsmönstret när testklassen äger disponibla resurser. Detta är ett förväntat beteende och du bör följa analysatorns vägledning.

Slutför testnivåbeställning

  1. Skapa en instans av testklassen (konstruktor)
  2. Ange TestContext egenskap (om det finns)
  3. Kör GlobalTestInitialize metoder
  4. Utför TestInitialize-metoder (från bas till härledd)
  5. Kör testmetod
  6. Uppdatera TestContext med resultat (till exempel Outcome egenskap)
  7. Kör TestCleanup metoder (från härledda till bas)
  8. Kör GlobalTestCleanup metoder
  9. Kör DisposeAsync (om det implementeras)
  10. Kör Dispose (om det implementeras)

Tips/Råd

Relaterade analysverktyg:

  • MSTEST0008 – validerar TestInitialize signaturen.
  • MSTEST0009 – validerar TestCleanup signaturen.
  • MSTEST0063 – validerar konstruktorn för testklassen.

Metodtips

  1. Använd lämpligt omfång: Placera konfigurationen på den högsta nivån som är lämplig för att undvika redundant arbete.

  2. Håll installationen snabb: Långvarig installation påverkar alla tester. Överväg lat initiering för dyra resurser.

  3. Rensa ordentligt: Rensa alltid resurser för att förhindra testinterferens och minnesläckor.

  4. Hantera asynkront på rätt sätt: Använd async Task returtyper, inte async void, för asynkrona metoder.

  5. Överväg testisolering: Varje test bör vara oberoende. Undvik delat föränderligt tillstånd mellan tester.

  6. Använd GlobalTest sparsamt: Globala livscykelmetoder körs för varje test, så håll dem lätta.

Se även