xUnit.net: Global kurulum + sökme?


102

Bu soru xUnit.net birim test çerçevesi ile ilgilidir .

Herhangi bir test yürütülmeden önce bazı kodları ve ayrıca tüm testler tamamlandıktan sonra bazı kodları çalıştırmam gerekiyor. Global başlatma ve sonlandırma kodunu belirtmek için bir tür öznitelik veya işaret arabirimi olması gerektiğini düşündüm, ancak bulamadım.

Alternatif olarak, xUnit'i programlı olarak çağırırsam, aşağıdaki kodla istediğimi de elde edebilirim:

static void Main()
{
    try
    {
        MyGlobalSetup();
        RunAllTests();  // What goes into this method?
    }
    finally
    {
        MyGlobalTeardown();
    }
}

Herhangi biri bana bazı genel kurulum / sökme kodlarının bildirimsel veya programlı olarak nasıl çalıştırılacağı konusunda bir ipucu verebilir mi?


Yanıtlar:


122

Bildiğim kadarıyla, xUnit'in global bir başlatma / sökme uzatma noktası yok. Ancak, bir tane oluşturmak kolaydır. IDisposableYapıcıda başlatmanızı ve IDisposable.Disposeyöntemde sökülmenizi uygulayan ve gerçekleştiren bir temel test sınıfı oluşturun . Bu şöyle görünecektir:

public abstract class TestsBase : IDisposable
{
    protected TestsBase()
    {
        // Do "global" initialization here; Called before every test method.
    }

    public void Dispose()
    {
        // Do "global" teardown here; Called after every test method.
    }
}

public class DummyTests : TestsBase
{
    // Add test methods
}

Ancak, her çağrı için temel sınıf kurulumu ve sökme kodu yürütülür. Çok verimli olmadığı için bu, istediğiniz şey olmayabilir. Daha optimize bir sürüm, IClassFixture<T>arabirimi, genel başlatma / sökme işlevinin yalnızca bir kez çağrılmasını sağlamak için kullanır . Bu sürüm için, test sınıfınızdan bir temel sınıfı genişletmezsiniz, ancak fikstür sınıfınıza atıfta bulunan IClassFixture<T>arabirimi uygularsınız T:

using Xunit;

public class TestsFixture : IDisposable
{
    public TestsFixture ()
    {
        // Do "global" initialization here; Only called once.
    }

    public void Dispose()
    {
        // Do "global" teardown here; Only called once.
    }
}

public class DummyTests : IClassFixture<TestsFixture>
{
    public DummyTests(TestsFixture data)
    {
    }
}

Bu , kurucunun TestsFixturetest edilen her sınıf için yalnızca bir kez çalıştırılmasıyla sonuçlanacaktır . Bu nedenle, iki yöntem arasında tam olarak ne seçmek istediğinize bağlıdır.


4
Görünüşe göre, IUseFixture artık mevcut değil, IClassFixture ile değiştirildi.
GaTechThomas

9
Bu işe yararken, Geir Sagberg'in cevabındaki CollectionFixture'ın bu senaryo için özel olarak tasarlandığı için daha uygun olduğunu düşünüyorum. Ayrıca test sınıflarınızı miras almak zorunda değilsiniz, bunları şu [Collection("<name>")]nitelikle işaretlemeniz yeterlidir
MichelZ

9
Eşzamansız kurulum ve sökme yapmanın bir yolu var mı?
Andrii

Görünüşe göre MS, IClassFixture çözümünü de uygulamış. docs.microsoft.com/en-us/aspnet/core/test/...
lbrahim

3
XUnit üç başlatma seçeneği sunar: test yöntemi başına, test sınıfı başına ve birkaç test sınıfını kapsayan. Belgeler burada: xunit.net/docs/shared-context
GHN

50

Aynı cevabı arıyordum ve şu anda xUnit belgeleri, geliştiricilere sınıf veya sınıflar grubu düzeyinde çok çeşitli kurulum / sökme işlevselliği sağlayan Sınıf Fikstürlerinin ve Koleksiyon Fikstürlerinin nasıl uygulanacağı konusunda çok yardımcı oluyor. Bu, Geir Sagberg'in cevabına uygundur ve neye benzemesi gerektiğini göstermek için iyi bir iskelet uygulaması sağlar.

https://xunit.github.io/docs/shared-context.html

Koleksiyon Fikstürleri Ne zaman kullanılmalı: Tek bir test içeriği oluşturmak ve bunu birkaç test sınıfındaki testler arasında paylaşmak ve test sınıflarındaki tüm testler bittikten sonra temizletmek istediğinizde.

Bazen bir fikstür nesnesini birden fazla test sınıfı arasında paylaşmak isteyeceksiniz. Sınıf armatürleri için kullanılan veritabanı örneği harika bir örnektir: bir test verileri kümesiyle bir veritabanını başlatmak ve ardından bu test verilerini birden çok test sınıfı tarafından kullanılmak üzere yerinde bırakmak isteyebilirsiniz. Birkaç test sınıfındaki testler arasında tek bir nesne örneğini paylaşmak için xUnit.net'in toplama fikstürü özelliğini kullanabilirsiniz.

Koleksiyon armatürlerini kullanmak için aşağıdaki adımları uygulamanız gerekir:

Fikstür sınıfını oluşturun ve başlangıç ​​kodunu fikstür sınıfı yapıcısına yerleştirin. Fikstür sınıfının temizleme gerçekleştirmesi gerekiyorsa, fikstür sınıfında IDisposable uygulayın ve temizleme kodunu Dispose () yöntemine yerleştirin. Koleksiyon tanımlama sınıfını oluşturun, [CollectionDefinition] özniteliğiyle süsleyerek, ona test koleksiyonunu tanımlayacak benzersiz bir ad verin. Koleksiyon tanımı sınıfına ICollectionFixture <> ekleyin. Test koleksiyonu tanımlama sınıfının [CollectionDefinition] özelliğine sağladığınız benzersiz adı kullanarak, koleksiyonun parçası olacak tüm test sınıflarına [Collection] özelliğini ekleyin. Test sınıflarının fikstür örneğine erişmesi gerekiyorsa, bunu bir yapıcı argümanı olarak ekleyin ve otomatik olarak sağlanacaktır. İşte basit bir örnek:

public class DatabaseFixture : IDisposable
{
    public DatabaseFixture()
    {
        Db = new SqlConnection("MyConnectionString");

        // ... initialize data in the test database ...
    }

    public void Dispose()
    {
        // ... clean up test data from the database ...
    }

    public SqlConnection Db { get; private set; }
}

[CollectionDefinition("Database collection")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture>
{
    // This class has no code, and is never created. Its purpose is simply
    // to be the place to apply [CollectionDefinition] and all the
    // ICollectionFixture<> interfaces.
}

[Collection("Database collection")]
public class DatabaseTestClass1
{
    DatabaseFixture fixture;

    public DatabaseTestClass1(DatabaseFixture fixture)
    {
        this.fixture = fixture;
    }
}

[Collection("Database collection")]
public class DatabaseTestClass2
{
    // ...
}

xUnit.net, toplama armatürlerini sınıf armatürleri ile aynı şekilde ele alır, ancak bir koleksiyon fikstürü nesnesinin ömrü daha uzundur: koleksiyondaki herhangi bir test sınıfında herhangi bir test çalıştırılmadan önce oluşturulur ve temizlenmez koleksiyondaki tüm test sınıflarının çalışması bitene kadar.

Test koleksiyonları ayrıca IClassFixture <> ile de dekore edilebilir. xUnit.net bunu, test koleksiyonundaki her bir test sınıfı, sınıf armatürü ile dekore edilmiş gibi ele alır.

Test koleksiyonları, xUnit.net'in paralel olarak çalıştırırken testleri çalıştırma şeklini de etkiler. Daha fazla bilgi için bkz. Testleri Paralel Olarak Çalıştırma.

Önemli not: Fikstürler, onları kullanan test ile aynı montajda olmalıdır.


1
"Test koleksiyonları ayrıca IClassFixture <> ile dekore edilebilir. XUnit.net bunu, test koleksiyonundaki her bir test sınıfı, sınıf armatürü ile dekore edilmiş gibi ele alır." Bunun bir örneğini alma şansım var mı? Ben tam olarak anlamıyorum.
rtf

@TannerFaulkner Sınıf fikstürü, bir Test Başlatma yöntemine sahip olduğunuzda geleneksel bir .net Birim Test Projesinde elde edeceğiniz gibi, SINIF düzeyinde bir kurulum ve sökme yapmanın bir yoluydu: [TestInitialize] public void Initialize () {
Larry Smith

Bununla ilgili tek sorunum Collection, "genel" kurulumun gerçekleşmesi için test sınıflarınızı öznitelikle süslemeniz gerektiğidir. Bu, -herhangi- test çalıştırılmadan önce kurmak istediğiniz herhangi bir şeye sahipseniz, -all-test sınıflarını bu öznitelikle dekore etmeniz gerektiği anlamına gelir. Tek bir test sınıfını dekore etmeyi unutmak, izini sürmesi zor olan hatalara yol açabileceğinden, bu bence çok kırılgan. XUnit gerçekten küresel kurulum ve sökme için bir yol oluştursa iyi olurdu.
Zodman

13

Kolay ve kolay bir çözüm var. Fody.ModuleInit eklentisini kullanın

https://github.com/Fody/ModuleInit

Bu bir nuget paketidir ve onu kurduğunuzda ModuleInitializer.csprojeye çağrılan yeni bir dosya ekler . Burada, derlemeden sonra derlemeye dokunan ve montaj yüklenir yüklenmez ve herhangi bir şey çalıştırılmadan önce çalıştırılan bir statik yöntem vardır.

Bunu, satın aldığım bir kitaplığa yazılım lisansının kilidini açmak için kullanıyorum. Her testte lisansı açmayı unutuyordum ve hatta testi kilidini açacak bir temel sınıftan türetmeyi bile unutuyordum. Bu kitaplığı yazan parlak kıvılcımlar, size lisans kilitli olduğunu söylemek yerine, testlerin başarısız olmalarına veya olmaması gerektiğinde geçmelerine neden olan ince sayısal hatalar ortaya çıkardı. Kitaplığı doğru bir şekilde açıp açmadığınızı asla bilemezsiniz. Şimdi benim modül başlangıcım şöyle görünüyor

/// <summary>
/// Used by the ModuleInit. All code inside the Initialize method is ran as soon as the assembly is loaded.
/// </summary>
public static class ModuleInitializer
{
    /// <summary>
    /// Initializes the module.
    /// </summary>
    public static void Initialize()
    {
            SomeLibrary.LicenceUtility.Unlock("XXXX-XXXX-XXXX-XXXX-XXXX");
    }
}

ve bu derlemeye yerleştirilen tüm testlerin lisansı onlar için doğru bir şekilde açılacaktır.


2
Sağlam fikir; ne yazık ki henüz DNX birim testleri ile çalışmıyor gibi görünüyor.
Jeff Dunlop

12

SetUp / TearDown kodunu birden çok sınıf arasında paylaşmak için xUnit's CollectionFixture'ı kullanabilirsiniz .

Alıntı:

Koleksiyon armatürlerini kullanmak için aşağıdaki adımları uygulamanız gerekir:

  • Fikstür sınıfını oluşturun ve başlangıç ​​kodunu fikstür sınıfı yapıcısına yerleştirin.
  • Fikstür sınıfının temizleme gerçekleştirmesi gerekiyorsa, fikstür sınıfında IDisposable uygulayın ve temizleme kodunu Dispose () yöntemine yerleştirin.
  • Koleksiyon tanımlama sınıfını oluşturun, [CollectionDefinition] özniteliğiyle süsleyerek, ona test koleksiyonunu tanımlayacak benzersiz bir ad verin.
  • Koleksiyon tanımı sınıfına ICollectionFixture <> ekleyin.
  • Test koleksiyonu tanımlama sınıfının [CollectionDefinition] özelliğine sağladığınız benzersiz adı kullanarak, koleksiyonun parçası olacak tüm test sınıflarına [Collection] özelliğini ekleyin.
  • Test sınıflarının fikstür örneğine erişmesi gerekiyorsa, bunu bir yapıcı argümanı olarak ekleyin ve otomatik olarak sağlanacaktır.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.