Birim testlerini seri olarak yürütün (paralel yerine)


115

Yazmış olduğum bir WCF ana bilgisayar yönetim motorunu birim test etmeye çalışıyorum. Motor temelde, konfigürasyona bağlı olarak anında ServiceHost örnekleri oluşturur. Bu, yeni bir hizmet eklendiğinde veya eski bir hizmet kaldırıldığında, tüm hizmetlerin kapatılmasına ve yeniden başlatılmasına gerek kalmadan hangi hizmetlerin mevcut olduğunu dinamik olarak yeniden yapılandırmamızı sağlar.

Bununla birlikte, ServiceHost'un çalışma şekli nedeniyle bu ana bilgisayar yönetim motorunu test etmede bir güçlükle karşılaştım. Bir ServiceHost önceden oluşturulmuş, açılmış ve belirli bir uç nokta için henüz kapatılmamışsa, aynı uç nokta için başka bir ServiceHost oluşturulamaz ve bu da bir istisnaya neden olur. Modern birim test platformlarının test yürütmelerini paralel hale getirmesi nedeniyle, bu kod parçasını birim test etmenin etkili bir yolu yok.

Genişletilebilirliği nedeniyle, testleri seri olarak çalıştırmaya zorlamanın bir yolunu bulabileceğimi umarak xUnit.NET kullandım. Ancak hiç şansım olmadı. Burada SO'daki birinin benzer bir sorunla karşılaştığını ve birim testlerinin seri olarak nasıl çalıştırılacağını bildiğini umuyorum.

NOT: ServiceHost , Microsoft tarafından yazılmış bir WCF sınıfıdır. Onun davranışını değiştirme yeteneğim yok. Her hizmet uç noktasını yalnızca bir kez barındırmak da doğru davranıştır ... ancak, özellikle birim testi için elverişli değildir.


ServiceHost'un bu belirli davranışı, ele almak isteyebileceğiniz bir şey olmaz mıydı?
Robert Harvey

ServiceHost, Microsoft tarafından yazılmıştır. Onun üzerinde hiçbir kontrolüm yok. Ve teknik olarak konuşursak, bu geçerli bir davranış ... uç nokta başına asla birden fazla ServiceHost'a sahip olmamalısınız.
jrista

1
TestServerDocker'da birden çok çalıştırmaya çalışırken benzer bir sorun yaşadım . Bu yüzden entegrasyon testlerini serileştirmem gerekti.
h-rai

Yanıtlar:


138

Her test sınıfı benzersiz bir test koleksiyonudur ve altındaki testler sırayla çalışacaktır, bu nedenle tüm testlerinizi aynı koleksiyona koyarsanız, sırayla çalışacaktır.

XUnit'te bunu başarmak için aşağıdaki değişiklikleri yapabilirsiniz:

Aşağıdakiler paralel olarak çalışacaktır:

namespace IntegrationTests
{
    public class Class1
    {
        [Fact]
        public void Test1()
        {
            Console.WriteLine("Test1 called");
        }

        [Fact]
        public void Test2()
        {
            Console.WriteLine("Test2 called");
        }
    }

    public class Class2
    {
        [Fact]
        public void Test3()
        {
            Console.WriteLine("Test3 called");
        }

        [Fact]
        public void Test4()
        {
            Console.WriteLine("Test4 called");
        }
    }
}

Sıralı hale getirmek için her iki test sınıfını da aynı koleksiyonun altına koymanız yeterlidir:

namespace IntegrationTests
{
    [Collection("Sequential")]
    public class Class1
    {
        [Fact]
        public void Test1()
        {
            Console.WriteLine("Test1 called");
        }

        [Fact]
        public void Test2()
        {
            Console.WriteLine("Test2 called");
        }
    }

    [Collection("Sequential")]
    public class Class2
    {
        [Fact]
        public void Test3()
        {
            Console.WriteLine("Test3 called");
        }

        [Fact]
        public void Test4()
        {
            Console.WriteLine("Test4 called");
        }
    }
}

Daha fazla bilgi için bu bağlantıya başvurabilirsiniz


29
Takdir edilmeyen cevap sanırım. Çalışıyor gibi görünüyor ve tek bir derlemede paralelleştirilebilir ve paralelleştirilemez testlerim olduğu için ayrıntı düzeyini seviyorum.
Igand

1
Bunu yapmanın doğru yolu bu, ref Xunit belgeleri.
Håkon K. Olafsen

2
Bu kabul edilen cevap olmalıdır, çünkü tipik olarak bazı testler paralel olarak çalıştırılabilir (benim durumumda tüm birim testleri), ancak bazıları paralel olarak çalıştırıldığında rastgele başarısız olur (benim durumumda bellek içi web istemcisi / sunucusu kullananlar), yani biri İstenirse test çalışmasını optimize edebilir.
Alexei

2
Bu, sqlite veritabanıyla entegrasyon testleri yaptığım bir .net çekirdek projesinde benim için işe yaramadı. Testler hala paralel olarak yürütüldü. Kabul edilen cevap yine de işe yaradı.
user1796440

Bu cevap için çok teşekkür ederim! Bunu, hem aynı TestBase'den miras alan hem de eşzamanlılığın EF Core ile iyi oynamadığı farklı sınıflarda Kabul Testlerine sahip olduğum için yapmam gerekiyordu.
Kyanite

111

Önemli: Bu yanıt .NET Framework için geçerlidir. Dotnet çekirdeği için Dimitry'nin cevabına bakın xunit.runner.json.

Tüm iyi birim testleri% 100 izole edilmelidir. Paylaşılan durumun kullanılması (örneğin static, her test tarafından değiştirilen bir özelliğe bağlı olarak) kötü uygulama olarak kabul edilir.

Bunu söyledikten sonra, xUnit testlerini sırayla çalıştırmayla ilgili sorunuzun bir cevabı var! Tam olarak aynı sorunla karşılaştım çünkü sistemim statik bir servis bulucu kullanıyor (ki bu idealden daha az).

Varsayılan olarak xUnit 2.x, tüm testleri paralel olarak çalıştırır. Bu, CollectionBehaviortest projenizdeki AssemblyInfo.cs dosyanızda tanımlanarak derleme başına değiştirilebilir .

Montaj başına ayırma kullanımı için:

using Xunit;
[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]

veya hiç paralelleştirme için kullanılmaz:

[assembly: CollectionBehavior(DisableTestParallelization = true)]

İkincisi muhtemelen istediğiniz şeydir. Paralelleştirme ve yapılandırma hakkında daha fazla bilgi xUnit belgelerinde bulunabilir .


5
Benim için her sınıftaki yöntemler arasında paylaşılan kaynaklar vardı. Bir sınıftan ve ardından diğerinden bir test çalıştırmak, her ikisinin de testlerini bozacaktır. Kullanarak çözebildim [assembly: CollectionBehavior(CollectionBehavior.CollectionPerClass, DisableTestParallelization = true)]. Sayende @Squiggle, tüm testleri yapıp kahve içebilirim! :)
Alielson Piffer

2
Abhinav Saxena'nın yanıtı, .NET Core için daha ayrıntılıdır.
Yennefer

78

.NET Core projeleri için xunit.runner.jsonşununla oluşturun :

{
  "parallelizeAssembly": false,
  "parallelizeTestCollections": false
}

Ayrıca, csprojiçermeli

<ItemGroup>
  <None Update="xunit.runner.json"> 
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </None>
</ItemGroup>

Eski .Net Core projeleri için project.jsonşunları içermelidir:

"buildOptions": {
  "copyToOutput": {
    "include": [ "xunit.runner.json" ]
  }
}

2
En son csproj dotnet çekirdek eşdeğerinin <ItemGroup><None Include="xunit.runner.json" CopyToOutputDirectory="Always" /></ItemGroup>ya da benzer olacağını varsayıyorum ?
Squiggle

3
Bu benim için csproj'da çalıştı:<ItemGroup> <None Update="xunit.runner.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup>
skynyrd

Paralelleştirmeyi devre dışı bırakmak xUnit Theories ile çalışır mı?
John Zabroski

Bu benim için çalışan tek şeydi, koşmayı denedim dotnet test --no-build -c Release -- xunit.parallelizeTestCollections=falseama benim için işe yaramadı.
harvzor

19

.NET Core projeleri için, xUnit'i https://xunit.github.io/docs/configuring-with-json.html adresindexunit.runner.json belgelendiği gibi bir dosya ile yapılandırabilirsiniz .

Paralel test yürütmeyi durdurmak için değiştirmeniz gereken ayar parallelizeTestCollections, varsayılan olarak şudur true:

trueMontaj, bu montaj içinde birbirine paralel olarak testler yapmaya istekli ise bunu olarak ayarlayın . ... falseBu test derlemesindeki tüm paralelleştirmeyi devre dışı bırakmak için bunu ayarlayın .

JSON şema türü: boolean
Varsayılan değer:true

Yani xunit.runner.jsonbu amaç için bir asgari

{
    "parallelizeTestCollections": false
}

Dokümanlarda belirtildiği gibi, bu dosyayı aşağıdaki yollarla yapınıza eklemeyi unutmayın:

  • Ayar Output Directory Kopyala için eğer kopyala yeni dosyanın içinde Özellikleri Visual Studio veya içinde
  • Ekleme

    <Content Include=".\xunit.runner.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    

    sizin için .csprojdosyaya veya

  • Ekleme

    "buildOptions": {
      "copyToOutput": {
        "include": [ "xunit.runner.json" ]
      }
    }
    

    senin için project.jsondosyanın

proje türünüze bağlı olarak.

Son olarak, yukarıdakilere ek olarak, Visual Studio kullanıyorsanız, Testleri Paralel Olarak Çalıştır düğmesini yanlışlıkla tıklamadığınızdan emin olun; bu, paralelizasyonu içinde kapatmış olsanız bile testlerin paralel olarak çalışmasına neden olur xunit.runner.json. Microsoft'un kullanıcı arabirimi tasarımcıları, yanlışlıkla vurma şansınızı en üst düzeye çıkarmak ve neden testlerinizin neden olduğu hakkında hiçbir fikriniz olmadan Test Gezgini'ndeki "Tümünü Çalıştır" düğmesinden yaklaşık bir santimetre uzakta bu düğmeyi etiketsiz, fark edilmesi zor ve bir santimetre uzağa yaptı aniden başarısız oluyor:

Düğme daire içine alınmış ekran görüntüsü


@JohnZabroski Önerilen düzenlemenizi anlamıyorum . ReSharper'ın herhangi bir şeyle ne ilgisi var? Sanırım yukarıdaki cevabı yazdığımda muhtemelen yükledim, ama buradaki her şey sizin onu kullanıp kullanmadığınızdan bağımsız değil mi? Ne gelmez zaman düzenleme bağlantı veren sayfa bir belirtme ile yapmak zorunda xunit.runner.jsondosyasını? Ve xunit.runner.jsontestlerin seri olarak yürütülmesini sağlamakla ne yapmalı?
Mark Amery

Testlerimi seri olarak çalıştırmaya çalışıyorum ve başlangıçta sorunun ReSharper ile ilgili olduğunu düşündüm (çünkü ReSharper'da Visual Studio Test Gezgini gibi "Testleri Paralel Olarak Çalıştır" düğmesi YOKTUR). Ancak [Teori] 'yi kullandığımda, testlerim izole değil gibi görünüyor. Bu garip çünkü okuduğum her şey bir Sınıfın paralelleştirilebilir en küçük birim olduğunu gösteriyor.
John Zabroski

11

Bu eski bir soru ama benim gibi yeni arayan kişilere bir çözüm yazmak istedim :)

Not: Bu yöntemi xunit 2.4.1 sürümü ile Dot Net Core WebUI entegrasyon testlerinde kullanıyorum.

NonParallelCollectionDefinitionClass adlı boş bir sınıf oluşturun ve ardından aşağıdaki gibi bu sınıfa CollectionDefinition özniteliğini verin. (Önemli kısım, DisableParallelization = doğru ayardır.)

using Xunit;

namespace WebUI.IntegrationTests.Common
{
    [CollectionDefinition("Non-Parallel Collection", DisableParallelization = true)]
    public class NonParallelCollectionDefinitionClass
    {
    }
}

Daha sonra aşağıdaki gibi paralel çalışmasını istemediğiniz sınıfa Collection niteliğini ekleyin. (Önemli kısım koleksiyonun adıdır. Koleksiyon Tanımlamasında kullanılan adla aynı olmalıdır)

namespace WebUI.IntegrationTests.Controllers.Users
{
    [Collection("Non-Parallel Collection")]
    public class ChangePassword : IClassFixture<CustomWebApplicationFactory<Startup>>
    ...

Bunu yaptığımızda öncelikle diğer paralel testler çalıştırılır. Bundan sonra Collection ("Non-Parallel Collection") özniteliğine sahip diğer testler çalıştırılır.


6

Oynatma Listesini Kullanabilirsiniz

test yöntemine sağ tıklayın -> Oynatma listesine ekle -> Yeni oynatma listesi

daha sonra yürütme sırasını belirtebilirsiniz, varsayılan olarak bunları yürütme listesine eklediğinizde, ancak çalma listesi dosyasını istediğiniz gibi değiştirebilirsiniz

görüntü açıklamasını buraya girin


5

Ayrıntıları bilmiyorum ama görünüşe göre birim testi yerine entegrasyon testi yapmaya çalışıyor olabilirsiniz . Bağımlılığı izole edebilirseniz , bu muhtemelen testinizi daha kolay (ve daha hızlı) yapacaktır. Yani (örneğin) aşağıdakileri bağımsız olarak test edebilirsiniz:ServiceHost

  • Yapılandırma okuma sınıfı
  • ServiceHost fabrikası (muhtemelen bir entegrasyon testi olarak)
  • Bir IServiceHostFactoryve bir alan motor sınıfıIConfiguration

İzolasyon (alay) çerçevelerini ve (isteğe bağlı olarak) IoC konteyner çerçevelerini dahil etmeye yardımcı olacak araçlar. Görmek:


Entegrasyon testi yapmaya çalışmıyorum. Gerçekten birim testi yapmam gerekiyor. TDD / BDD terimleri ve uygulamaları (IoC, DI, Mocking, vb.) Konusunda çok bilgiliyim, bu nedenle fabrikalar oluşturmak ve arayüzler kullanmak gibi değirmenlerin çalışması ihtiyacım olan şey değil (zaten yapıldı, ServiceHost'un kendisi dışında.) ServiceHost, düzgün bir şekilde alay edilemediği için izole edilebilecek bir bağımlılık değildir (.NET Sistem ad alanlarının çoğu gibi.) Birim testlerini seri olarak çalıştırmak için gerçekten bir yola ihtiyacım var.
jrista

1
@jrista - becerilerinizle ilgili hiçbir şey düşünülmemişti. Bir WCF geliştiricisi değilim, ancak motorun, sarmalayıcıda bir arabirimle ServiceHost çevresinde bir sarmalayıcı döndürmesi mümkün olabilir mi? Veya belki ServiceHost'lar için özel bir fabrika?
TrueWill

Barındırma motoru herhangi bir ServiceHost döndürmez. Aslında hiçbir şey döndürmez, yalnızca ServiceHost'ların dahili olarak oluşturulmasını, açılmasını ve kapatılmasını yönetir. Tüm temel WCF türlerini paketleyebilirim, ancak bu gerçekten yapmaya yetkili olmadığım BİR ÇOK iş. Ayrıca, ortaya çıktığı gibi, sorun paralel yürütmeden kaynaklanmıyor ve normal çalışma sırasında yine de olacak. Burada SO'da sorunla ilgili başka bir soru başlattım ve umarım bir cevap alırım.
jrista

@TrueWill: BTW, becerilerimi küçümsemenden hiç endişelenmedim ... Sadece birim testiyle ilgili tüm yaygın şeyleri kapsayan çok sayıda sıradan yanıt almak istemedim. Çok özel bir sorun için hızlı bir cevaba ihtiyacım vardı. Biraz kaba davrandıysam özür dilerim, niyetim bu değildi. Bu şeyi çalıştırmak için oldukça sınırlı zamanım var.
jrista

3

Belki Advanced Unit Testing'i kullanabilirsiniz . Testi çalıştırdığınız sırayı tanımlamanıza olanak tanır . Dolayısıyla, bu testleri barındırmak için yeni bir cs dosyası oluşturmanız gerekebilir.

Test yöntemlerini istediğiniz sırada çalışması için nasıl bükebileceğiniz aşağıda açıklanmıştır.

[Test]
[Sequence(16)]
[Requires("POConstructor")]
[Requires("WorkOrderConstructor")]
public void ClosePO()
{
  po.Close();

  // one charge slip should be added to both work orders

  Assertion.Assert(wo1.ChargeSlipCount==1,
    "First work order: ChargeSlipCount not 1.");
  Assertion.Assert(wo2.ChargeSlipCount==1,
    "Second work order: ChargeSlipCount not 1.");
  ...
}

Çalışıp çalışmadığını bana bildirin.


Harika makale. Aslında CP'ye işaretledim. Bağlantı için teşekkürler, ancak ortaya çıktığı gibi, test koşucuları testleri paralel olarak çalıştırmadıkları için sorun çok daha derin görünüyor.
jrista

2
Bekle, önce testin paralel çalışmasını istemediğini söylüyorsun ve sonra sorunun test koşucularının testleri paralel olarak çalıştırmaması olduğunu söylüyorsun ... peki hangisi?
Graviton

Sağladığınız bağlantı artık çalışmıyor. Ve bu xunit ile yapabileceğiniz bir şey mi?
Allen Wang


0

Bir temel sınıfa [Koleksiyon ("Sıralı")] özelliğini ekledim :

    namespace IntegrationTests
    {
      [Collection("Sequential")]
      public class SequentialTest : IDisposable
      ...


      public class TestClass1 : SequentialTest
      {
      ...
      }

      public class TestClass2 : SequentialTest
      {
      ...
      }
    }

0

Şimdiye kadar önerilen cevapların hiçbiri benim için işe yaramadı. XUnit 2.4.1 ile bir dotnet çekirdek uygulamam var. Bunun yerine her birim testine bir kilit koyarak bir geçici çözümle istenen davranışı elde ettim. Benim durumumda, çalışma düzeni umrumda değildi, sadece testler sıralıydı.

public class TestClass
{
    [Fact]
    void Test1()
    {
        lock (this)
        {
            //Test Code
        }
    }

    [Fact]
    void Test2()
    {
        lock (this)
        {
            //Test Code
        }
    }
}
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.