MSTest yaşam döngüsü

MSTest, test sınıfları ve test yöntemleri için iyi tanımlanmış bir yaşam döngüsü sağlar ve test yürütmenin çeşitli aşamalarında kurulum ve kaldırma işlemleri gerçekleştirmenizi sağlar. Yaşam döngüsünü anlamak verimli testler yazmanıza ve yaygın tuzaklardan kaçınmanıza yardımcı olur.

Yaşam döngüsüne genel bakış

Yaşam döngüsü, en yüksek düzeyden (derleme) en düşük düzeye (test yöntemi) yürütülen dört aşamaya ayrılır:

  1. Derleme düzeyi: Test derlemesi yüklendiğinde ve kaldırıldığında bir kez çalışır
  2. Sınıf düzeyi: Test sınıfı başına bir kez çalışır
  3. Genel test düzeyi: Derlemedeki her test yönteminden önce ve sonra çalışır
  4. Test düzeyi: Her test yöntemi için çalışır (parametreli testlerdeki her veri satırı dahil)

Derleme düzeyinde yaşam döngüsü

Test derlemesi yüklenip kaldırıldığında derleme yaşam döngüsü yöntemleri bir kez çalıştırılır. Veritabanı başlatma veya hizmet başlatma gibi tek seferlik pahalı kurulumlar için bunları kullanın.

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

Gereksinimler

  • Yöntemler şu şekilde olmalıdır: public static
  • Dönüş türü: void, Taskveya ValueTask (MSTest v3.3+)
  • AssemblyInitialize bir TestContext parametre gerektirir
  • AssemblyCleanup sıfır parametre veya bir TestContext parametre kabul eder (MSTest 3.8+)
  • Her öznitelikten yalnızca birine derleme başına izin verilir.
  • [TestClass] ile işaretlenmiş bir sınıfta olmalıdır

Tavsiye

İlgili çözümleyiciler:

  • MSTEST0012 - imzayı doğrular AssemblyInitialize .
  • MSTEST0013 - imzayı doğrular AssemblyCleanup .

Sınıf düzeyinde yaşam döngüsü

Sınıf yaşam döngüsü yöntemleri, o sınıftaki tüm test yöntemlerinden önce ve sonra test sınıfı başına bir kez çalışır. Bir sınıftaki testler arasında paylaşılan kurulum için bunları kullanın.

ClassInitialize ve 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()
    {
        HttpResponseMessage response = await _client!.GetAsync("/users");
        Assert.IsTrue(response.IsSuccessStatusCode);
    }
}

Gereksinimler

  • Yöntemler şu şekilde olmalıdır: public static
  • Dönüş türü: void, Taskveya ValueTask (MSTest v3.3+)
  • ClassInitialize bir TestContext parametre gerektirir
  • ClassCleanup sıfır parametre veya bir TestContext parametre kabul eder (MSTest 3.8+)
  • Sınıf başına izin verilen her özniteliklerden yalnızca biri

Devralma davranışı

Türetilmiş sınıfların çalıştırılıp çalıştırılmayacağını ClassInitialize kullanarak InheritanceBehavior denetleyin.

[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
    }
}
DevralmaDavranışı Description
None (varsayılan) Başlatma yalnızca beyan eden sınıf için çalıştırılır.
BeforeEachDerivedClass Her türetilmiş sınıftan önce işlemleri başlatma

Tavsiye

İlgili çözümleyiciler:

  • MSTEST0010 - imzayı doğrular ClassInitialize .
  • MSTEST0011 - İmzayı doğrular ClassCleanup .
  • MSTEST0034 - ClassCleanupBehavior.EndOfClass kullanılmasını önerir.

Genel test düzeyi yaşam döngüsü

Uyarı

Genel test yaşam döngüsü öznitelikleri MSTest 3.10.0'da sunulmuştur.

Genel test yaşam döngüsü yöntemleri, her test sınıfına kod eklemeye gerek kalmadan derlemenin tamamında her test yönteminden önce ve sonra çalışır.

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

Gereksinimler

  • Yöntemler şu şekilde olmalıdır: public static
  • Dönüş türü: void, Taskveya ValueTask
  • Tam olarak bir TestContext parametre olmalıdır
  • [TestClass] ile işaretlenmiş bir sınıfta olmalıdır
  • Derlemede bu özniteliklere sahip birden çok yönteme izin verilir

Uyarı

Birden çok GlobalTestInitialize veya GlobalTestCleanup yöntem mevcut olduğunda yürütme sırası garanti değildir. TimeoutAttribute yöntemlerinde GlobalTestInitialize desteklenmez.

Tavsiye

İlgili çözümleyici: MSTEST0050 - genel test fikstür yöntemlerini doğrular.

Test seviyesi yaşam döngüsü

Her test yöntemi için test düzeyinde yaşam döngüsü çalışır. Parametreli testler için yaşam döngüsü her veri satırı için çalışır.

Kurulum aşaması

Test başına kurulum için TestInitialize veya bir yapıcı kullanın.

[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()
    {
        int result = _calculator!.Add(2, 3);
        Assert.AreEqual(5, result);
    }
}

Oluşturucu ve TestInitialize karşılaştırması:

Görünüş Yapıcı TestInitialize
Asenkron destek Hayı Yes
Zaman aşımı desteği Hayı Evet ([Timeout] özniteliği ile)
Yürütme sırası First Oluşturucudan sonra
Inheritance Temel sonra türetilir Temel sonra türetilir
Özel durum davranışı Temizleme ve Atma çalışmıyor (örnek yok) Temizlik ve İmha işlemleri hala çalışıyor

Tavsiye

Hangi yaklaşımı kullanmalıyım? Oluşturucular genellikle tercih edilir çünkü alanları readonly kullanmanıza olanak tanır ve bu da değişmezliği sağlar ve test sınıfınızın anlaşılmasını kolaylaştırır. Zaman uyumsuz başlatma veya zaman aşımı desteğine ihtiyacınız olduğunda TestInitialize kullanın.

Ayrıca her iki yaklaşımı da birleştirebilirsiniz: readonly alanlarını zaman uyumlu olarak basit bir şekilde başlatmak için oluşturucuyu kullanın ve bu alanlara bağımlı ek zaman uyumsuz kurulum için TestInitialize'i kullanın.

tutarlı bir yaklaşımı zorunlu kılmak için isteğe bağlı olarak kod çözümleyicilerini etkinleştirebilirsiniz:

  • MSTEST0019 - Oluşturucular yerine TestInitialize yöntemlerini tercih edin
  • MSTEST0020 - TestInitialize yöntemleri yerine oluşturucuları tercih edin

Yürütme aşaması

Kurulum tamamlandıktan sonra test yöntemi yürütülür. Test yöntemleri için async MSTest döndürülen Task veya ValueTasköğesini bekler.

Uyarı

Zaman uyumsuz test yöntemlerinin varsayılan olarak bir SynchronizationContext değeri yoktur. Bu, kullanıcı arabirimi iş parçacığında çalışan UWP ve WinUI'deki testler için UITestMethod geçerli değildir.

Temizleme aşaması

Test başına temizleme için TestCleanup veya IDisposable/IAsyncDisposable kullanın.

[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()
    {
        HttpResponseMessage response = await _client!.GetAsync("https://example.com");
        Assert.IsTrue(response.IsSuccessStatusCode);
    }
}

Temizleme yürütme sırası (temel olarak türetilir):

  1. TestCleanup (türetilmiş sınıf)
  2. TestCleanup (temel sınıf)
  3. DisposeAsync (uygulandıysa)
  4. Dispose (uygulandıysa)

Tavsiye

tutarlı bir temizleme yaklaşımını zorunlu kılmak için isteğe bağlı olarak kod çözümleyicilerini etkinleştirebilirsiniz:

  • MSTEST0021 - TestCleanup yöntemleri yerine Dispose'ı tercih edin
  • MSTEST0022 - Dispose yöntemleri yerine TestCleanup'ı tercih edin

.NET kod analizi kuralları gibi MSTest dışı çözümleyiciler etkinleştirildiyse, test sınıfınız atılabilir kaynaklara sahip olduğunda atma desenini uygulamanızı öneren CA1001 görebilirsiniz. Bu beklenen bir davranıştır ve çözümleyicinin yönergelerini izlemeniz gerekir.

Test düzeyi sırasını tamamla

  1. Test sınıfının örneğini oluşturma (oluşturucu)
  2. Özelliği ayarla TestContext (varsa)
  3. Çalıştır GlobalTestInitialize yöntemleri
  4. Metotları çalıştır TestInitialize (tabandan türetilmişe)
  5. Test yöntemini yürütme
  6. TestContext sonuçlarla güncelleştir (örneğin, bir Outcome özelliği)
  7. Çalıştırma TestCleanup yöntemleri (temel olarak türetilir)
  8. Çalıştır GlobalTestCleanup yöntemleri
  9. Çalıştır DisposeAsync (uygulandıysa)
  10. Çalıştır Dispose (uygulandıysa)

Tavsiye

İlgili çözümleyiciler:

En iyi yöntemler

  1. Uygun kapsamı kullanın: Yedekli çalışmalardan kaçınmak için kurulumu anlamlı olan en üst düzeye yerleştirin.

  2. Kurulumu hızlı tutun: Uzun süre çalışan kurulum tüm testleri etkiler. Pahalı kaynaklar için yavaş başlatmayı göz önünde bulundurun.

  3. Düzgün temizleme: Test müdahalesini ve bellek sızıntılarını önlemek için kaynakları her zaman temizleyin.

  4. Test yalıtımını göz önünde bulundurun: Her test bağımsız olmalıdır. Testler arasında paylaşılan değişken durumdan kaçının.

  5. GlobalTest'i tedbirli kullanın: Genel yaşam döngüsü yöntemleri her test için çalışır, bu nedenle bunları hafif tutun.

Ayrıca bakınız