Jalankan tes unit secara serial (bukan secara paralel)

99

Saya mencoba untuk menguji unit mesin manajemen host WCF yang telah saya tulis. Mesin pada dasarnya membuat instance ServiceHost dengan cepat berdasarkan konfigurasi. Hal ini memungkinkan kami untuk secara dinamis mengkonfigurasi ulang layanan mana yang tersedia tanpa harus mematikan semuanya dan memulai ulang setiap kali layanan baru ditambahkan atau layanan lama dihapus.

Saya mengalami kesulitan dalam pengujian unit mesin manajemen host ini, bagaimanapun, karena cara kerja ServiceHost. Jika ServiceHost telah dibuat, dibuka, dan belum ditutup untuk titik akhir tertentu, ServiceHost lain untuk titik akhir yang sama tidak dapat dibuat, yang mengakibatkan pengecualian. Karena fakta bahwa platform pengujian unit modern memparalelkan eksekusi pengujian mereka, saya tidak memiliki cara efektif untuk menguji unit kode ini.

Saya telah menggunakan xUnit.NET, berharap karena ekstensibilitasnya, saya dapat menemukan cara untuk memaksanya menjalankan pengujian secara serial. Namun, saya belum beruntung. Saya berharap bahwa seseorang di sini di SO telah mengalami masalah serupa dan tahu cara menjalankan pengujian unit secara serial.

CATATAN: ServiceHost adalah kelas WCF, yang ditulis oleh Microsoft. Saya tidak memiliki kemampuan untuk mengubah perilakunya. Menghosting setiap titik akhir layanan hanya sekali juga merupakan perilaku yang tepat ... namun, ini tidak terlalu kondusif untuk pengujian unit.

jrista
sumber
Bukankah perilaku ServiceHost ini menjadi sesuatu yang mungkin ingin Anda tangani?
Robert Harvey
ServiceHost ditulis oleh Microsoft. Saya tidak punya kendali atasnya. Dan secara teknis, ini adalah perilaku yang valid ... Anda tidak boleh memiliki lebih dari satu ServiceHost per titik akhir.
jrista
1
Saya mengalami masalah serupa saat mencoba menjalankan beberapa TestServerdi buruh pelabuhan. Jadi saya harus membuat serial tes integrasi.
h-rai

Jawaban:

117

Setiap kelas pengujian adalah kumpulan pengujian unik dan pengujian di bawahnya akan dijalankan secara berurutan, jadi jika Anda meletakkan semua pengujian Anda dalam koleksi yang sama maka itu akan berjalan secara berurutan.

Di xUnit Anda dapat membuat perubahan berikut untuk mencapai ini:

Berikut ini akan berjalan secara paralel:

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

Untuk membuatnya berurutan, Anda hanya perlu meletakkan kedua kelas pengujian di bawah koleksi yang sama:

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

Untuk info lebih lanjut, Anda dapat merujuk ke tautan ini

Abhinav Saxena
sumber
24
Jawaban yang kurang dihargai, saya pikir. Tampaknya berfungsi dan saya suka perinciannya, karena saya memiliki tes yang dapat diparalelkan dan tidak dapat disejajarkan dalam satu perakitan.
Igand
1
Ini adalah cara yang benar untuk melakukan ini, ulang dokumentasi Xunit.
Håkon K. Olafsen
2
Ini harus menjadi jawaban yang diterima karena biasanya beberapa tes dapat dijalankan secara paralel (dalam kasus saya semua tes unit), tetapi beberapa gagal secara acak ketika dijalankan secara paralel (dalam kasus saya yang menggunakan klien / server web dalam memori), jadi salah satunya adalah dapat mengoptimalkan pengujian yang berjalan jika diinginkan.
Alexei
2
Ini tidak berhasil untuk saya dalam proyek inti .net tempat saya melakukan tes integrasi dengan database sqlite. Tes masih dilakukan secara paralel. Jawaban yang diterima berhasil.
pengguna1796440
Terima kasih banyak atas jawaban ini! Diperlukan untuk melakukan ini karena saya memiliki Tes Penerimaan di kelas berbeda yang keduanya diwarisi dari TestBase yang sama dan konkurensi tidak berjalan baik dengan EF Core.
Kyanite
104

Seperti yang dinyatakan di atas, semua unit test yang baik harus 100% terisolasi. Menggunakan status bersama (misalnya bergantung pada staticproperti yang dimodifikasi oleh setiap pengujian) dianggap sebagai praktik yang buruk.

Karena itu, pertanyaan Anda tentang menjalankan pengujian xUnit secara berurutan memang memiliki jawabannya! Saya mengalami masalah yang persis sama karena sistem saya menggunakan pencari layanan statis (yang kurang dari ideal).

Secara default xUnit 2.x menjalankan semua tes secara paralel. Ini dapat dimodifikasi per-assembly dengan menentukan CollectionBehaviordi AssemblyInfo.cs Anda dalam proyek pengujian Anda.

Untuk penggunaan pemisahan per rakitan:

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

atau tanpa paralelisasi sama sekali, gunakan:

[assembly: CollectionBehavior(DisableTestParallelization = true)]

Yang terakhir mungkin yang Anda inginkan. Informasi lebih lanjut tentang paralelisasi dan konfigurasi dapat ditemukan di dokumentasi xUnit .

Coretan
sumber
5
Bagi saya, ada sumber daya bersama antar metode dalam setiap kelas. Menjalankan pengujian dari satu kelas, lalu satu dari yang lain, akan menghentikan pengujian dari keduanya. Saya bisa menyelesaikannya dengan menggunakan [assembly: CollectionBehavior(CollectionBehavior.CollectionPerClass, DisableTestParallelization = true)]. Terima kasih kepada Anda, @Squiggle, saya dapat menjalankan semua pengujian saya dan pergi minum kopi! :)
Alielson Piffer
2
Jawaban dari Abhinav Saxena lebih terperinci untuk .NET Core.
Yennefer
67

Untuk proyek .NET Core, buat xunit.runner.jsondengan:

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

Juga, Anda csprojharus mengandung

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

Untuk proyek .Net Core lama, Anda project.jsonharus berisi

"buildOptions": {
  "copyToOutput": {
    "include": [ "xunit.runner.json" ]
  }
}
Dmitry Polomoshnov
sumber
2
Saya berasumsi setara inti dotnet csproj terbaru akan <ItemGroup><None Include="xunit.runner.json" CopyToOutputDirectory="Always" /></ItemGroup>atau serupa?
Squiggle
3
Ini berhasil untuk saya di csproj:<ItemGroup> <None Update="xunit.runner.json"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> </ItemGroup>
skynyrd
Apakah menonaktifkan paralelisasi berfungsi dengan Teori xUnit?
John Zabroski
Ini adalah satu-satunya hal yang berhasil untuk saya, saya mencoba berlari seperti dotnet test --no-build -c Release -- xunit.parallelizeTestCollections=falsetetapi tidak berhasil untuk saya.
Harvey
18

Untuk proyek .NET Core, Anda dapat mengkonfigurasi xUnit dengan xunit.runner.jsonfile, seperti yang didokumentasikan di https://xunit.github.io/docs/configuring-with-json.html .

Setelan yang perlu Anda ubah untuk menghentikan eksekusi uji paralel adalah parallelizeTestCollections, yang defaultnya adalah true:

Setel ini truejika rakitan bersedia menjalankan tes di dalam rakitan ini secara paralel satu sama lain. ... Setel ini untuk falsemenonaktifkan semua paralelisasi dalam perakitan uji ini.

Jenis skema JSON: boolean
Nilai default:true

Jadi minimal xunit.runner.jsonuntuk tujuan ini sepertinya

{
    "parallelizeTestCollections": false
}

Seperti disebutkan di dokumen, ingatlah untuk menyertakan file ini dalam build Anda, baik dengan:

  • Mengatur Salin ke Direktori Output untuk Menyalin jika file itu lebih baru Properti di Visual Studio, atau
  • Menambahkan

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

    untuk Anda .csproj file , atau

  • Menambahkan

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

    untuk Anda project.json file

tergantung pada jenis proyek Anda.

Terakhir, selain di atas, jika Anda menggunakan Visual Studio, pastikan Anda tidak secara tidak sengaja mengklik tombol Run Tests In Parallel , yang akan menyebabkan pengujian berjalan secara paralel meskipun Anda telah menonaktifkan paralelisasi di xunit.runner.json. Desainer UI Microsoft dengan cerdik membuat tombol ini tidak berlabel, sulit untuk diperhatikan, dan sekitar satu sentimeter dari tombol "Run All" di Test Explorer, hanya untuk memaksimalkan kemungkinan Anda akan menekannya secara tidak sengaja dan tidak tahu mengapa pengujian Anda tiba-tiba gagal:

Tangkapan layar dengan tombol dilingkari

Mark Amery
sumber
@JohnZabroski Saya tidak memahami suntingan yang Anda sarankan . Apa hubungan ReSharper dengan semua hal? Saya pikir saya mungkin telah menginstalnya ketika saya menulis jawaban di atas, tetapi bukankah semuanya di sini terlepas dari apakah Anda menggunakannya atau tidak? Apa hubungan halaman yang Anda tautkan dalam pengeditan dengan menentukan xunit.runner.jsonfile? Dan apa hubungannya menentukan xunit.runner.jsondengan membuat pengujian dijalankan secara serial?
Mark Amery
Saya mencoba untuk menjalankan pengujian saya secara serial, dan awalnya mengira masalah tersebut terkait dengan ReSharper (karena ReSharper TIDAK memiliki tombol "Jalankan Pengujian secara Paralel" seperti yang dimiliki Visual Studio Test Explorer). Namun, tampaknya ketika saya menggunakan [Teori], pengujian saya tidak terisolasi. Ini aneh, karena semua yang saya baca menunjukkan bahwa Kelas adalah unit terkecil yang dapat diparalelkan.
John Zabroski
9

Ini pertanyaan lama tetapi saya ingin menulis solusi untuk orang-orang yang mencari baru seperti saya :)

Catatan: Saya menggunakan metode ini dalam tes integrasi Dot Net Core WebUI dengan xunit versi 2.4.1.

Buat kelas kosong bernama NonParallelCollectionDefinitionClass lalu berikan atribut CollectionDefinition ke kelas ini seperti di bawah ini. (Bagian penting adalah DisableParallelization = true setting.)

using Xunit;

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

Setelah itu tambahkan atribut Collection ke kelas yang tidak Anda inginkan berjalan secara paralel seperti di bawah ini. (Bagian yang penting adalah name of collection. Harus sama dengan nama yang digunakan di CollectionDefinition)

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

Saat kami melakukan ini, pertama-tama pengujian paralel lainnya dijalankan. Setelah itu pengujian lain yang menjalankan atribut Collection ("Non-Parallel Collection").

Gündüz Emre ÖZER
sumber
6

Anda dapat Menggunakan Daftar Putar

klik kanan pada metode pengujian -> Tambahkan ke daftar putar -> Daftar putar baru

kemudian Anda dapat menentukan urutan eksekusi, defaultnya adalah, saat Anda menambahkannya ke daftar putar tetapi Anda dapat mengubah file daftar putar sesuai keinginan

masukkan deskripsi gambar di sini

HB MAAM
sumber
5

Saya tidak tahu detailnya, tetapi sepertinya Anda mencoba melakukan pengujian integrasi daripada pengujian unit . Jika Anda dapat mengisolasi ketergantungan pada ServiceHost, itu kemungkinan akan membuat pengujian Anda lebih mudah (dan lebih cepat). Jadi (misalnya) Anda dapat menguji yang berikut ini secara independen:

  • Konfigurasi kelas membaca
  • Pabrik ServiceHost (mungkin sebagai uji integrasi)
  • Kelas mesin yang membutuhkan IServiceHostFactorydanIConfiguration

Alat yang akan membantu mencakup kerangka kerja isolasi (tiruan) dan kerangka kerja kontainer IoC (opsional). Lihat:

TrueWill
sumber
Saya tidak mencoba melakukan pengujian integrasi. Saya memang perlu melakukan pengujian unit. Saya benar-benar berpengalaman dalam persyaratan dan praktik TDD / BDD (IoC, DI, Mocking, dll.), Jadi menjalankan berbagai hal seperti membuat pabrik dan menggunakan antarmuka bukanlah yang saya butuhkan (sudah selesai, kecuali dalam kasus ServiceHost itu sendiri.) ServiceHost bukanlah ketergantungan yang dapat diisolasi, karena tidak dapat diolok-olok dengan benar (seperti banyak ruang nama .NET System.) Saya benar-benar membutuhkan cara untuk menjalankan pengujian unit secara serial.
jrista
1
@jrista - tidak ada sedikit pun keterampilan Anda yang dimaksudkan. Saya bukan pengembang WCF, tetapi apakah mungkin mesin mengembalikan pembungkus di sekitar ServiceHost dengan antarmuka pada pembungkusnya? Atau mungkin pabrik khusus untuk ServiceHosts?
TrueWill
Mesin hosting tidak mengembalikan ServiceHost apa pun. Ini sebenarnya tidak mengembalikan apa pun, itu hanya mengelola pembuatan, pembukaan, dan penutupan ServiceHosts secara internal. Saya bisa membungkus semua tipe WCF fundamental, tapi itu BANYAK pekerjaan yang belum benar-benar diizinkan untuk saya lakukan. Selain itu, ternyata, masalah tersebut bukan disebabkan oleh eksekusi paralel, dan akan tetap terjadi selama pengoperasian normal. Saya memulai pertanyaan lain di sini di SO tentang masalah tersebut, dan mudah-mudahan saya akan mendapatkan jawaban.
jrista
@TrueWill: BTW, saya tidak khawatir tentang Anda meremehkan keterampilan saya sama sekali ... Saya hanya tidak ingin mendapatkan banyak jawaban run-of-the-mill yang mencakup semua hal umum tentang pengujian unit. Saya membutuhkan jawaban cepat untuk masalah yang sangat spesifik. Maaf jika saya sedikit kasar, bukan maksud saya. Saya hanya memiliki waktu terbatas untuk membuat hal ini berfungsi.
jrista
3

Mungkin Anda bisa menggunakan Pengujian Unit Lanjutan . Ini memungkinkan Anda untuk menentukan urutan Anda menjalankan pengujian . Jadi, Anda mungkin harus membuat file cs baru untuk menghosting pengujian tersebut.

Berikut cara membengkokkan metode pengujian agar berfungsi dalam urutan yang Anda inginkan.

[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.");
  ...
}

Beri tahu saya apakah ini berhasil.

Graviton
sumber
Artikel bagus. Sebenarnya saya sudah membookmarknya di CP. Terima kasih untuk tautannya, tetapi ternyata, masalahnya tampaknya jauh lebih dalam, karena pelari tes tampaknya tidak menjalankan tes secara paralel.
jrista
2
Tunggu, pertama Anda mengatakan Anda tidak ingin tes berjalan secara paralel, dan kemudian Anda mengatakan bahwa masalahnya adalah pelari tes tidak menjalankan tes secara paralel ... jadi yang mana?
Graviton
Tautan yang Anda berikan tidak lagi berfungsi. Dan apakah ini sesuatu yang dapat Anda lakukan dengan xunit?
Allen Wang
1

Bagi saya, dalam aplikasi .Net Core Console, ketika saya ingin menjalankan metode pengujian (bukan kelas) secara sinkron, satu-satunya solusi yang berhasil adalah yang dijelaskan di blog ini: xUnit: Control the Test Execution Order

pengguna3057544
sumber
0

Saya telah menambahkan atribut [Collection ("Sequential")] di kelas dasar:

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


      public class TestClass1 : SequentialTest
      {
      ...
      }

      public class TestClass2 : SequentialTest
      {
      ...
      }
    }
Matías Romero
sumber
0

Sejauh ini tidak ada jawaban yang disarankan untuk saya. Saya memiliki aplikasi inti dotnet dengan XUnit 2.4.1. Saya mencapai perilaku yang diinginkan dengan solusi dengan menempatkan kunci di setiap pengujian unit sebagai gantinya. Dalam kasus saya, saya tidak peduli tentang urutan berjalan, hanya saja pengujiannya berurutan.

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

    [Fact]
    void Test2()
    {
        lock (this)
        {
            //Test Code
        }
    }
}
Matt Hayes
sumber