Nunit: Fitur instance-per-test-case

Dibuat pada 24 Nov 2017  ·  112Komentar  ·  Sumber: nunit/nunit

Model mental semua orang tampaknya adalah bahwa ada instance yang dibuat per kasus uji saat memparalelkan dalam perlengkapan (https://github.com/nunit/nunit/issues/2105, https://github.com/nunit/nunit/issues /2252, https://github.com/nunit/nunit/issues/2573) meskipun ini belum pernah terjadi. Saya cenderung memecahkan masalah ini dengan selalu menggunakan kelas statis dan mendefinisikan kelas perlengkapan bersarang sekali pakai yang memberikan pengalaman yang lebih kaya dalam beberapa hal ( contoh ), tetapi itu mungkin bukan yang kami inginkan untuk semua orang. Kelemahan kecil di sini adalah dalam terminologi, bahwa kelas statis adalah apa yang NUnit anggap sebagai perlengkapan tetapi perlengkapan sebenarnya adalah kelas bersarang.

Ini bukan opsi untuk menjadikan instance-per-test-case sebagai default karena itu merusak perlengkapan non-paralel yang bergantung pada satu pengujian untuk dapat mengakses status dari pengujian lain. Orang-orang yang menggunakan atribut [Order] mungkin adalah satu-satunya orang yang akan terpengaruh, tetapi itu benar-benar akan merusak semua tes yang bergantung pada pesanan yang dapat saya pikirkan.

Saya suka proposal Charlie:

  • Atribut tingkat perakitan yang memungkinkan pengaturan cara perlengkapan uji dibuat menjadi instance tunggal atau instance per perlengkapan.
  • Instance Tunggal akan berfungsi seperti sekarang
  • Contoh per perlengkapan akan membuat perlengkapan baru untuk setiap tes
  • BAIK Lakukan aktivitas penyiapan dan pembongkaran satu kali untuk setiap pengujian (yaitu tidak lagi "satu kali") ATAU menyebabkan OneTimeSetUp dan OneTimeTearDown ditandai sebagai kesalahan.

Kemungkinan ekstensi:

  • Buat level kelas ini juga, memungkinkan perlengkapan individu memiliki siklus hidup yang berbeda.
  • Memiliki pengaturan tingkat perakitan ketiga yang secara otomatis menjalankan instans terpisah untuk perlengkapan apa pun yang berisi satu atau beberapa pengujian yang dijalankan secara paralel. (Sulit untuk selalu tahu sebelumnya)

Saya akan mengatakan lakukan dasar-dasarnya terlebih dahulu.

Saat ini dijamin menjadi bug jika ada keadaan instance yang bisa berubah dan fixture berjalan secara paralel dengan dirinya sendiri, jadi kita bisa memiliki opsi Auto yang beralih ke instance-per-test-case jika test case dari fixture dikonfigurasi untuk dijalankan secara paralel dan menghindari perubahan yang melanggar . Saya pikir Auto berpotensi membingungkan, tetapi mungkin juga merupakan default yang paling masuk akal sehingga semua orang tidak akan menambahkan [assembly: InstancePerTestCase] sebagai hal rutin.

Saya mendukung pagar pembatas. OneTimeSetUp dan OneTimeTearDown harus ditandai sebagai kesalahan jika perlengkapan melakukan instance per kasus uji. Mungkin kecuali mereka statis?

done feature normal docs releasenotes

Komentar yang paling membantu

ada kemungkinan kita bisa meninjau ini? rasanya seperti benar-benar dekat

Semua 112 komentar

Saat ini, Anda __can__ berbagi status di antara pengujian paralel asalkan (1) Anda menyetel status dalam konstruktor atau penyiapan satu kali dan (2) tidak mengubahnya dalam pengujian apa pun. Anda bahkan dapat bertahan dalam beberapa kasus jika status disetel ke konstanta dalam pengujian atau penyiapan, asalkan Anda tidak pernah menyetelnya ke nilai lain. Yang terakhir ini menjijikkan, meskipun saya pikir kami memiliki beberapa tes NUnit yang melakukannya.

Pikiran saya adalah bahwa kita tidak dapat mengubah default dalam waktu dekat, jika pernah. Memiliki status yang dapat berubah saat ini tidak menjamin bug. Ini hanya bug jika Anda benar-benar mengubahnya dan kami tidak tahu itu kecuali kami melakukan analisis kode - yang tidak boleh dilakukan!

Jika Anda tidak mengubah status instans apa pun dalam pengujian atau penyiapan atau pembongkaran, Anda tidak akan rusak. Jika ya, Anda sudah rusak selama tes Anda dapat berjalan secara paralel dengan diri mereka sendiri. Tidak mungkin Anda tidak hancur yang bisa saya pikirkan. Itulah pembenaran untuk opsi Otomatis, jika menurut kami kerumitan yang dihasilkan bagi pengguna sepadan dengan hasil "default yang masuk akal".

Anda ada benarnya tetapi saya akan terus menganjurkan bahwa ini adalah perpanjangan potensial, yang tidak perlu kita putuskan sekarang. Mari kita lakukan ini secara berulang daripada mencoba membuat semua keputusan sekarang.

saya dengan @CharliePoole defaultnya harus tetap apa adanya dan tidak dapat dihapus jika tidak, itu dapat merusak orang yang menginginkan perilaku itu.

mungkin atribut tingkat perlengkapan untuk memicu instance per utas daripada instance yang dibagikan untuk semua utas.

@BlythMeister Jangan khawatir! Kami tidak mempertimbangkan perubahan apa pun yang akan menyebabkan kode yang ada mulai rusak.

Ini menarik. Saya pikir instance-per-test-case lebih mudah untuk dipikirkan daripada instance-per-thread. Cara-cara di mana tes ditugaskan ke utas tidak dapat diandalkan, jadi saya tidak dapat memikirkan manfaat apa pun yang akan lebih dari instance-per-test-case.

ya saya menggunakan istilah utas yang berarti eksekusi paralel .... yang sebenarnya tidak akurat.
permintaan maaf saya.

Dalam istilah XP (dan saya harap semua orang tahu itu selalu perspektif saya) kami memiliki tiga cerita, yang dibangun di atas satu sama lain ...

  1. Pengguna dapat menentukan perilaku instance-per-test-case secara global untuk semua perlengkapan dalam perakitan.

  2. Pengguna dapat menentukan perilaku instance-per-test-case pada fixture.

  3. Pengguna dapat menentukan perilaku instance-per-test-case untuk perlengkapan yang berisi pengujian yang dapat berjalan secara paralel.

Saya ingin melihat (1) sepenuhnya diterapkan dalam masalah ini. Saya pikir kita harus mengeluarkan (2) dari kepala kita - atau setidaknya dari bagian yang menulis kode - untuk saat ini. Saya pikir (3) adalah suatu kemungkinan tetapi memiliki beberapa jebakan yang telah kami temui dalam masalah lain. (Perlengkapan tidak tahu apakah itu akan menjalankan kasus uji secara paralel!) Mari kita bicara tentang JustInTime itu, yang akan terjadi setelah (1) dan (2) selesai dan digabungkan. FWIW, saya pikir kita memiliki kecenderungan untuk terikat dengan mencoba memikirkan beberapa langkah ke depan. Saya menyebutkan (2) dan (3) sehingga kami tidak akan memblokirnya dengan cara apa pun, yang tampaknya cukup mudah untuk dihindari.

Saya pikir kita semua setuju saat itu. Saya memiliki beberapa prioritas NUnit sebelum ini, jadi saya akan membiarkan orang lain mengomentari dan mungkin mengambilnya.

Keindahan melakukan ini dengan atribut daripada pengaturan baris perintah adalah bahwa itu akan berfungsi untuk pelari mana pun.

Saya setuju dengan @CharliePoole bahwa itu harus menjadi atribut. Pengujian perlu dirancang dengan mempertimbangkan instance per pengujian atau instance per fixture, sehingga pengujian harus selalu berjalan seperti itu. Saya juga lebih suka untuk hanya melakukan opsi 1. Saya pikir jika Anda menginginkan gaya pengujian itu, Anda harus memilihnya untuk seluruh rangkaian pengujian Anda. Mengizinkan Anda untuk ikut serta di tingkat yang lebih rendah memperkenalkan kompleksitas yang menurut saya tidak memiliki ROI yang baik.

Ingin tahu apakah ada rencana dalam waktu dekat untuk mengimplementasikan fitur ini?

@rprouse Ini masih ditandai sebagai membutuhkan desain tetapi sepertinya diskusi sudah mencakup semuanya.

@CharliePoole setuju bahwa ini dapat beralih dari desain. @pfluegs kami belum mulai mengerjakan ini selain mendesainnya, jadi belum ada rencana.

Tidak yakin ada orang yang begitu memperhatikannya, tetapi tidak adanya label diskusi atau desain menyiratkan seseorang dapat menjadi sukarelawan. 😄

Saya baru saja digigit oleh masalah ini - +1 untuk diperbaiki.

Jika kami menginginkan lebih banyak opsi, apakah kami ingin:

```c#
[perakitan: FixtureCreation(FixtureCreationOptions.PerTestCase)]

And:
```diff
namespace NUnit.Framework
{
+   public enum FixtureCreationOptions
+   {
+       Singleton, // = default
+       PerTestCase
+   }
}

Pemikiran saya sendiri tentang ini telah sedikit berubah, setidaknya dalam konteks kerangka kerja baru.

Saya sekarang berpikir harus ada dua atribut tingkat kelas, satu untuk setiap perilaku. Saya akan membuat TestFixture berfungsi dengan instance per pengujian dan Shared Fixture berfungsi seperti NUnit sekarang. Jelas, itu adalah perubahan besar dalam konteks NUnit, tetapi pendekatan umum mungkin layak dipertimbangkan.

@CharliePoole [TestFixture(FixtureOptions.PerTestCase)] sepertinya sintaks yang bagus, tetapi saya juga sangat menginginkan atribut tingkat perakitan sehingga saya dapat ikut serta dengan setiap proyek pengujian. Seperti apa kita ingin atribut tingkat perakitan terlihat?

Benar, itu sebabnya saya menyatakan reservasi WRT apa yang akan saya lakukan pada kerangka kerja baru. Namun, dalam kasus NUnit, Anda tidak ingin mengubah semantik TestFixture dan saya tidak dapat memikirkan nama yang sangat bagus untuk perilaku instance-per-case. Jika Anda memiliki dua nama, yang harus Anda lakukan untuk mendapatkan perilaku yang diinginkan pada semua perlengkapan adalah melakukan pengeditan global pada file sumber Anda.

Saya tidak suka pengaturan global di tingkat Majelis karena akhirnya membingungkan beberapa pengembang, yang mungkin tidak mengetahui apa yang diatur dalam file AssemblyInfo. Saat ini, kami mengizinkan beberapa, jadi saya kira tidak ada salahnya menambahkan yang lain. Saya tidak memiliki pemikiran tentang nama untuk atribut tingkat Majelis kecuali bahwa itu harus mengkomunikasikan bahwa kami sedang menetapkan opsi (default?) untuk semua perlengkapan di Majelis.

Saya juga tidak menyukai default tingkat Majelis sampai batas tertentu, karena alasan yang Anda sebutkan. Dibandingkan dengan menambahkan atribut baru ke setiap kelas perlengkapan tanpa atribut saya saat ini, atribut tingkat perakitan adalah rute yang saya inginkan.

@nunit/framework-team Bagaimana menurut Anda? Untuk pengenalan awal fitur:

  • ya/tidak untuk pengaturan tingkat perakitan?
  • ya/tidak untuk pengaturan tingkat perlengkapan?
  • ya/tidak untuk pengaturan tingkat metode?
  • ya/tidak untuk menguji pengaturan tingkat kasus?

Poin bagus tentang kelas perlengkapan tanpa atribut.

Saya akan mengatakan ya, ya, tidak, tidak

Saya setuju dengan Charlie - dengan klarifikasi bahwa 'tingkat perlengkapan' berarti 'tingkat kelas'. (Berlawanan dengan misalnya metode sumber kasus uji. 😊)

Itu membantu! Jadi masalah dengan [TestFixture(FixtureCreationOptions.PerTestCase)] adalah bahwa beberapa atribut TestFixture diizinkan pada kelas yang sama dan kemudian dapat saling bertentangan. Lebih baik dan lebih sederhana mungkin memiliki atribut bernama baru yang berlaku untuk rakitan dan kelas?

```c#
[perakitan: ShareFixtureInstances (salah)]

[ShareFixtureInstances(salah)]
SomeFixture kelas publik
{
// ...
}

Framework API:

```diff
namespace NUnit.Framework
{
+   [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class)]
+   public sealed class ShareFixtureInstancesAttribute : PropertyAttribute
+   {
+       public bool ShareFixtureInstances { get; }

+       public ShareFixtureInstancesAttribute(bool shareFixtureInstances);
+   }
}

Kedengarannya bagus. Apa perilaku pewarisan ini - jika saya memiliki atribut pada kelas dasar, dengan tesnya sendiri, dan kemudian juga mewarisi dari kelas itu? 😊

Agaknya, karena ini adalah atribut 'kelas' - itu harus di kelas sebenarnya dari mana tes tersebut dijalankan?

Juga, saya tidak sepenuhnya tertarik pada namanya - karena fixture tidak selalu merupakan kelas dalam istilah nunit, saya tidak berpikir (misalnya semua tes dalam testcasesource - kecuali 'suite' dan 'fixture' memiliki semantik yang lebih kuat berarti daripada yang saya pikirkan, dalam istilah Nunit!)

Bagaimana dengan sesuatu di sepanjang baris InstancePerTest...tetapi lebih baik bernama...

Setuju tentang tingkat Majelis dan Kelas tetapi tidak lebih dalam. Saya juga lebih suka sesuatu yang sejalan dengan saran @ChrisMaddock ClassInstancePerTest(bool) . Saya juga berasumsi bahwa atribut di tingkat kelas akan menimpa tingkat Majelis dan saya berharap itu diwariskan. Saya pikir pewarisan penting karena Anda biasanya menginginkan instance per tes berdasarkan anggota kelas yang dikombinasikan dengan paralelisme. Karena itu Anda ingin itu diterapkan secara otomatis ke kelas turunan.

Saya pernah benar-benar terkejut NUnit tidak membuat instance per pengujian ketika jenis perlengkapan uji dapat dibangun. Saya selalu memikirkan unit test seperti, saya tidak tahu, unit test: Anda mengatur scaffolding, menjalankan tes, dan scaffolding hilang. Tidak ada transfer status yang tidak disengaja di antara pengujian.

Setelah mempelajari itu. Saya, seperti @jnm2 , instantiate tes secara manual. Tetapi ketika tes, misalnya membuat dan menghapus file, runner kasus menjadi sangat tidak praktis: mereka harus IDisposable; Tidak ada lagi [TearDown] ajaib untukmu. :( Tidak ada cara yang konsisten untuk membedakan kesalahan pengujian dari kesalahan perancah. Ini adalah kasus penggunaan yang sangat menyakitkan.

Jika saya dapat menerobos masuk pada desain, saya dengan @CharliePoole ya, ya, tidak, tidak.

Nama PerTestInstance , InstancePerTest atau sesuatu yang mirip dengan itu terdengar jauh lebih baik daripada ShareFixtureInstances . Saya agak tidak suka kata Class dalam nama atribut, seperti ketika saya menguji kode F#, saya tidak menganggap tipe instance menjadi class . Tapi yang pasti aku bisa hidup dengan itu! :)

Wah, nggak sabar nunggu fitur ini!

@kkm000 Saya setuju tentang penggunaan "kelas". NUnit biasanya menggunakan nama yang berbeda dari implementasinya. Meski begitu, orang membuat asumsi, tetapi lebih baik konsisten dalam menghindari hal-hal seperti itu.

Contoh dari apa yang saya bicarakan: Test, TestFixture, SetUpFixture, Worker (bukan thread), dll. Pada prinsipnya, kelas __could__ mewakili satu tes, bukan perlengkapan... Saya tidak mengatakan kami melakukan itu atau bahkan seharusnya, tapi __kita bisa__.

@kkm000

Terima kasih atas komentarnya! Anda tidak menerobos masuk sama sekali. Kami memiliki tujuan desain untuk menjadi agnostik bahasa jika memungkinkan.
Selain fokus pada C#, satu hal lagi tentang kata class : bagaimana jika suatu saat kita mendukung perlengkapan yang bukan kelas?

[InstancePerTestCase] akan menghindari masalah, tetapi mungkin kehilangan kejelasan intuitif.

Perlengkapan dan kelas hari ini tidak banyak berbeda, tetapi jika mereka berbeda, saya cukup yakin atribut ini berkaitan dengan perlengkapan dan bukan kelas.

Suite adalah konsep organisasi umum, sedangkan perlengkapan adalah hal-hal yang menyimpan logika penyiapan/penguraian dan status pengujian dan diteruskan ke metode pengujian (saat ini sebagai argumen this metode instan). Jadwal tidak harus menjadi bagian dari hierarki organisasi. Mari kita bayangkan perlengkapan menyimpang dari konsep kelas dan menyimpang dari konsep suite tes:

```c#
public static class SomeTestSuite // Bukan perlengkapan: tidak ada logika setup/teardown dan tidak ada status pengujian
{
[Test] // Tes tunggal, bukan rangkaian parameter;
// fixture disuntikkan melalui parameter alih-alih melalui parameter this implisit
public static void SomeTest (perlengkapan SomeFixture)
{
fixture.SomeService.SomeProperty = true;
perlengkapan.SomeAction();
perlengkapan.Menegaskan Sesuatu();
}
}

[Perlengkapan Tes]
struct publik SomeFixture : IDisposable
{
public SomeService SomeService { dapatkan; } // Negara

public SomeFixture() // This was almost legal syntax in C# 6, but I'm making a point 😁
{
    // Setup
}

public void Dispose()
{
    // Teardown
}

public void SomeAction()
{
    // Helper method
}

public void AssertSomething()
{
    // Helper method
}

}
```

Dalam contoh di atas, [FixtureInstancePerTestCase] atau [SharedFixtureInstances(false)] tidak fokus pada kelas dan tidak fokus pada hierarki (suite): fokus pada perlengkapan, dan perlengkapan adalah logika dan status penyiapan/teardown yang dapat digunakan kembali. Secara khusus, ini difokuskan pada apakah SomeFixture dibuat untuk setiap kasus pengujian atau apakah kasus yang sama diteruskan ke setiap pengujian.

Karena atribut ini akan mengontrol apakah ITests yang berbeda akan membagikan instance ITest.Fixture, dan hal yang dikontrol adalah apa pun yang kita hias dengan [TestFixture] (atribut lain dengan Fixture di namanya), sepertinya a _consistent_ opsi untuk menggunakan kata Fixture.

(Saya membawa poin-poin yang tampaknya penting untuk didiskusikan, tetapi saya tidak terlalu tertarik pada apa yang akhirnya menjadi nama itu.)

@ jnm2 Anda benar bahwa konsep suite dan perlengkapan dapat dipisahkan. NUnit, bagaimanapun, mengikuti contoh dari semua kerangka uji xunit yang ada pada saat dibuat dengan menggabungkan keduanya. Ini menyimpang dari kerangka kerja lain dalam membuat perlengkapan bersama.

Sepengetahuan saya, xunit.net adalah kerangka kerja pertama (satu-satunya?) yang menjadikan perlengkapan sebagai entitas terpisah dari hierarki pewarisan pengujian.

Ketika saya membuat tes parameter, saya bisa saja memperkenalkan TestCaseSource sebagai "perlengkapan" terpisah. Saya tidak melakukan itu dan saya pikir perubahan yang diperlukan untuk mengulanginya akan rusak sekarang.

Saya pikir alasan orang kadang-kadang (sebelum paralelisme) bertanya tentang perilaku contoh per kasus adalah karena itulah yang dilakukan kerangka kerja lain. Untuk alasan yang sama mereka __tidak__ meminta perlengkapan yang dapat dipisahkan.

Jadi, kesimpulannya, inilah yang saya pikirkan ...

  1. Kami membutuhkan contoh per perilaku kasus uji.

  2. Itu tidak bisa menjadi default karena kompatibilitas ke belakang.

  3. Perlengkapan yang dapat dipisahkan adalah ide yang keren, tetapi juga merupakan masalah yang terpisah.

  4. Perlengkapan yang dapat dipisahkan terlihat paling tidak rumit untuk diterapkan sesuai dengan perlengkapan instans. Tentu saja Anda tidak perlu berurusan dengan kompatibilitas mundur dalam kasus ini, yang membuatnya lebih mudah.

Hanya untuk mengonfirmasi tentang poin 3 dan 4, saya bermaksud sampel kode saya bukan sebagai saran untuk fitur baru tetapi sebagai eksperimen pemikiran yang dimaksudkan untuk membantu kami mendapatkan nama yang tepat untuk atribut ini. Eksperimen pikiran memberi tahu saya bahwa kami benar-benar fokus pada gagasan perlengkapan, bukan kelas atau suite, meskipun kebetulan saat ini bertepatan.

Apa pun bentuknya, perlengkapan adalah definisi pengaturan/penghancuran/status yang dapat digunakan kembali. Saya percaya itulah arti dari kata tersebut. Instans perlengkapan (kemunculan penyiapan/penghancuran/status) adalah apa yang diputuskan untuk dibagikan atau tidak dibagikan oleh atribut baru kami di antara kasus uji. Saya pikir FixtureInstances di suatu tempat dalam nama akan membawa kejelasan maksimum.

Apakah ini tampak benar untuk semua orang? Jika tidak, poin apa lagi yang bisa kita pertimbangkan?

Ah! Maaf, saya pikir Anda telah mencapai poin yang sangat bagus, biasanya diabaikan, tetapi saya juga takut kita mungkin menyimpang. :slightly_frowning_face:

Saya pikir nama lengkap (apa pun yang Anda ketik) harus menyertakan Fixture dan Instance. Cara melakukannya tergantung pada apakah Anda berbicara tentang nama atribut atau enum yang digunakan dalam membangunnya. Tidak yakin apakah Anda telah menetap di satu atau yang lain.

Ya, poin yang bagus.

Jika kita melakukan enum, itu membuatnya lebih lama untuk mengetik. Ini lebih penting untuk atribut bertarget kelas daripada yang bertarget Majelis. Kita juga harus menghindari menambahkan enum sebagai parameter ke TestFixtureAttribute karena beberapa TestFixtureAttributes diperbolehkan di kelas yang sama dan parameter yang satu mungkin bertentangan dengan parameter yang lain. Kami dapat menangani ini melalui kesalahan waktu pengujian, tetapi biasanya bagus untuk membuat status yang tidak valid tidak dapat diwakili.

Mari kita naikkan beberapa opsi dan kita dapat mengubahnya dari sana.
Kami mencari sesuatu yang menyeimbangkan intuisi, konsistensi dengan gaya NUnit, dan jumlah fleksibilitas yang tepat. (Sebagian besar, hal-hal ini seharusnya sudah tumpang tindih. )


Usulan A

```c#
[perakitan: FixtureOptions(InstancePerTestCase = true)]

[FixtureOptions(InstancePerTestCase = false)]
kelas SomeFixture
{
}

Pro: extending later if there's ever more options for fixture handling (doubtful?)
Pro: two names so there's less stuffed into a single name
Con: `[FixtureOptions]` would be legal but highly misunderstandable syntax. (Does it reset the assembly-wide setting to the default, `false`, or does it have no effect, e.g. `null`?)


### Proposal B

```c#
[assembly: FixtureOptions(FixtureCreationOptions.PerTestCase)]

[FixtureOptions(FixtureCreationOptions.Singleton)]
class SomeFixture
{
}

public enum FixtureCreationOptions
{
    Singleton, // = default
    PerTestCase,
    // Doubtful there will be other options?
    // Maybe Pool, where we reset and reuse between nonconcurrent tests?    
}

Pro: status yang tidak valid tidak dapat diwakili
Pro: diperpanjang nanti jika ada lebih banyak opsi untuk penanganan perlengkapan (diragukan?)
Con: redundansi dalam mengetik nama atribut dan nama enum

Usulan C

```c#
[perakitan: FixtureInstancePerTestCase]

[FixtureInstancePerTestCase(salah)]
kelas SomeFixture
{
}
```

Pro: status yang tidak valid tidak dapat diwakili
Pro: sulit untuk salah paham (tapi saya memiliki titik buta di sini karena saya bukan pengguna baru)
Con: nama panjang
Mungkin con: Nama yang sangat spesifik yang dapat menentukan opsi dan nilainya. (Tapi kami juga punya [NonParallelizable] yang saya suka.)


(Proposal alternatif dipersilakan!)

Ada sesuatu yang bisa dikatakan untuk memiliki dua atribut: satu untuk Majelis dan satu untuk kelas itu sendiri.

Pro: Lebih mudah untuk menerapkan dua atribut sederhana daripada satu atribut dengan perilaku yang berbeda dalam konteks. (Pelajaran dari ParallelizableAttribute)
Pro: Kemungkinan akan lebih mudah dipahami pengguna, karena masing-masing dapat diberi nama dengan tepat. Misalnya, tingkat perakitan dapat menyertakan kata "Default".
Con: Dua atribut untuk diingat.

Pendekatan menggunakan setiap alternatif Anda:

```C#
[perakitan: DefaultFixtureOptions(InstancePerTestCase = true)]

[FixtureOptions(InstancePerTestCase = false)]
kelas SomeFixture
{
}

[perakitan: DefaultFixtureOptions(FixtureCreationOptions.PerTestCase)]

[FixtureOptions(FixtureCreationOptions.Singleton)]
kelas SomeFixture
{
}

[perakitan: DefaultFixtureInstancePerTestCase]

[FixtureInstancePerTestCase(salah)]
kelas SomeFixture
{
}
```

Komentar lain:

  • Saya suka A dan B lebih baik daripada C
  • Anda tidak boleh menyebut perilaku perlengkapan bersama "Lajang", karena itu bukan satu. :smile: Jika Anda mewarisi, Anda akan mendapatkan beberapa contoh perlengkapan dasar.
  • Jika Anda menggunakan atribut terpisah di tingkat perakitan dan perlengkapan, Anda sebenarnya dapat melepaskan perlengkapan dari nama di tingkat perlengkapan.
  • Kami belum menentukan bagaimana SetUpFixtures cocok dengan ini sama sekali. Mereka bermasalah dan mungkin memerlukan kesalahan run-time jika atribut digunakan pada mereka. Faktanya, satu keuntungan dari atribut yang sepenuhnya terpisah adalah kita tidak harus berurusan dengan ini. (Ini juga merupakan keuntungan bagi properti TestFixture, BTW)
  • Kami perlu memperingatkan orang-orang bahwa mereka instance-per-testcase tidak berlaku untuk classd statis. Ini jelas bagi kita semua tetapi ada orang yang tidak akan segera memahaminya.

BTW, saya sering berharap kami menggunakan DefaultTestCaseTimeOut untuk tingkat perakitan dan perlengkapan dan menyimpan Timeout hanya untuk metode.

Bagus, terima kasih telah mengemukakan kemungkinan ini.

Sesuatu yang mengganggu saya saat ini adalah bahwa ini secara teoritis bisa masuk akal, meskipun kita tidak menuju ke arah ini:

c# [DefaultFixtureOptions(InstancePerTestCase = true)] class SomeFixture { [FixtureOptions(InstancePerTestCase = false)] public void SomeTest() { } }

Karena level di mana ini benar-benar dimainkan bukanlah level fixture itu sendiri melainkan level test case.
Sebagai contoh: perlengkapan turunan mungkin mengesampingkan pengaturan perlengkapan dasar, tetapi semua yang sebenarnya dilakukan adalah memengaruhi pengaturan untuk setiap kasus uji yang dihasilkan oleh kelas turunan.

Atau:

```c#
[perakitan: DefaultFixtureOptions(InstancePerTestCase = true)]

// Mengapa ini tidak dianggap sebagai default juga?
[DefaultFixtureOptions(InstancePerTestCase = true)]
BaseFixture kelas abstrak publik
{
}

kelas tertutup publik SomeFixture : BaseFixture
{
}
```

Bagaimana kita menemukan alasan kapan dan kapan tidak menggunakan Default ?

Saya pikir menerapkan atribut ini pada setiap kasus uji akan menimbulkan banyak kerumitan dengan hanya sedikit manfaat.

Untuk perlengkapan uji "normal", kami akan terus membuat instance instance sebagai bagian dari rantai perintah yang terkait dengan perlengkapan uji. Untuk perlengkapan instance-per-tes, kami tidak akan membuat instance di sana tetapi harus mengimplementasikan perintah baru sebagai bagian dari rantai pengujian, yang akan melakukan hal yang hampir sama dengan apa yang sekarang dilakukan di rantai perlengkapan.

Jika perlengkapan mungkin berisi kedua jenis pengujian, maka kita harus menjalankannya dua kali, sekali untuk setiap model.

Manfaatnya tampak kecil, karena pengguna dapat dengan mudah mencapai hal yang sama dengan membagi tes menjadi perlengkapan terpisah. Fixtures, bagaimanapun, adalah artefak NUnit dan masuk akal untuk meminta pengguna melakukan tes yang membutuhkan perilaku fixture yang sama ke dalam fixture yang sama.

Mengenai kapan harus menggunakan "Default"... menurut saya Anda akan __not__ menggunakannya pada TestFixxture mana pun dan Anda akan menggunakannya hanya pada tingkat perakitan. Kelas dasar bukan pengecualian karena atribut sebenarnya ditemukan pada kelas turunan berdasarkan pewarisan. Jadi mengatur perilaku tertentu di pangkalan bukanlah default. Melainkan pengaturan untuk kelas itu dan apa pun yang berasal darinya. Spesifikasi yang bertentangan harus menyebabkan kesalahan atau diabaikan, seperti yang kita lakukan dalam semua kasus atribut yang diwarisi saat ini. Artinya, menempatkan atribut A pada kelas dasar dan B pada kelas turunan tidak dibedakan dengan menempatkan A dan B secara langsung pada kelas dasar. Kami merancangnya seperti itu karena akan mengejutkan pengguna untuk melakukan hal lain.

Perlengkapan pengaturan WRT, btw, saya pikir itu harus menghasilkan kesalahan runtime untuk menentukan salah satu opsi instantiasi di sana. SetUpFixtures bukan TestFixtures.

Artinya, menempatkan atribut A pada kelas dasar dan B pada kelas turunan tidak dibedakan dengan menempatkan A dan B secara langsung pada kelas dasar. Kami merancangnya seperti itu karena akan mengejutkan pengguna untuk melakukan hal lain.

Ya ampun, saya akan mengharapkan [Apartment] atau [Parallelizable] untuk menggantikan pengaturan kelas dasar atau metode dasar!

Ini semua masuk akal bagi saya.

Bisakah kita mengesampingkan keinginan enum seperti FixtureCreationOptions ? PerTestCase , PerFixture , hipotetis ReusedPerTestCase ?

Saya condong ke kesederhanaan:

```c#
[perakitan: DefaultFixtureOptions(InstancePerTestCase = true)]

[FixtureOptions(InstancePerTestCase = false)]
kelas SomeFixture
{
}

Hypothetical other creation options would then be added this way:
```c#
[FixtureOptions(InstancePerTestCase = false, ReuseInstances = true)]

@nunit/framework-team Apa preferensi Anda?

Tentang

[assembly: DefaultFixtureOptions(InstancePerTestCase = true)]

Mungkin hanya saya, tetapi secara umum saya memahami atribut sebagai indikasi bahwa objek yang ditandai memiliki sifat tertentu, bukan sebaliknya; argumen ke atribut, jika ada, menyempurnakan sifat tersebut. Ambil, misalnya, Serializable--objek dapat serializable, tetapi jika tidak didekorasi, itu tidak; InternalsVisibleTo--internal terlihat oleh sesuatu yang spesifik, dan argumen memperbaikinya, jika tidak internal tidak terlihat oleh apa pun, dll. Beberapa atribut tidak mengikuti pola, tetapi ini biasanya level rendah, e, g, CompilationRepresentation, MethodImpl. Ini biasanya datang dengan argumen konstruktor yang diperlukan.

Sekarang pertimbangkan
c# [assembly: DefaultFixtureOptions]
Ini adalah C# yang terbentuk dengan baik, karena penugasan ke InstancePerTestCase adalah opsional. Apa yang diungkapkannya secara semantik?

Bagi saya, [assembly: DefaultFixtureInstancePerTestCase] tampaknya lebih alami (dengan pengecualian untuk nama sesquipedaliannya, tapi itu hanya pendapat, tidak dapat dibenarkan kecuali secara estetika). Satu atribut, satu sifat. Mungkin yang ini tidak memerlukan parameter apa pun atau properti yang dapat diatur secara opsional.

Juga, sebagai pengguna, saya terkejut bahwa ada dua atribut dengan nama yang berbeda, satu hanya berlaku di tingkat Majelis ( DefaultSomething ) dan satu lagi hanya pada tingkat tipe (hanya Something ) . Secara alami, saya tidak mengerti apa "pelajaran yang dipelajari" yang disebutkan oleh @CharliePoole mengenai atribut Parallelizable , tetapi dari sisi pulau ini, sepasang atribut untuk dipelajari di mana seseorang akan melakukannya (dalam pemikiran dangkal pengguna ini) tampaknya membingungkan.

@kkm000 Saya setuju dengan komentar Anda tentang atribut yang mengekspresikan satu sifat.

WRT dua atribut berbeda untuk sifat "sama" pada tingkat yang berbeda, saya __akan__ setuju jika saya pikir mereka sebenarnya sama. Tapi saya tidak berpikir mereka. Saya akan menggunakan Timeout sebagai contoh.

Jika Timeout ditempatkan pada metode pengujian itu berarti "Metode pengujian ini akan habis jika dibutuhkan lebih dari jumlah milidetik yang ditentukan untuk dijalankan." Namun, ketika Timeout ditempatkan pada fixture atau assembly, ini memiliki arti yang berbeda: "Timeout default untuk semua test case yang terkandung dalam item ini adalah pada jumlah milidetik yang ditentukan." Default itu, tentu saja, dapat diganti di level yang lebih rendah. Dua arti yang agak berbeda, yang omong-omong memiliki dua implementasi terpisah. Yang pertama menerapkan batas waktu untuk tes yang muncul. Yang kedua menyimpan default dalam konteks pengujian di mana ia akan ditemukan dan digunakan sebagai default ketika kasus uji yang terkandung ditemukan. ParallelizableAttribute bekerja dengan cara yang sama.

Dalam kasus khusus ini, ditempatkan pada fixture, atributnya berarti "Instantiate this fixture sekali per test case." Pada tingkat perakitan itu berarti "Buat Instansi Perlengkapan Uji di rakitan ini sekali per perlengkapan kecuali ditentukan lain."

Dalam kasus khusus ini, ditempatkan pada fixture, atributnya berarti "Instantiate this fixture sekali per test case." Pada tingkat perakitan itu berarti "Buat Instansi Perlengkapan Uji di rakitan ini sekali per perlengkapan kecuali ditentukan lain."

Dengan asumsi Anda salah bicara dalam kalimat terakhir--apakah _"Buat Instansiasi Perlengkapan Uji di rakitan ini sekali per kasus uji ~ perlengkapan~ kecuali ditentukan lain."_ dimaksudkan? Atau apakah saya benar-benar salah paham tentang ruang lingkup dan maksud?

Dengan asumsi saya tidak, bagi saya, sebagai pengguna, semantik tidak terlihat berbeda sama sekali: "buat kelas per tes dalam ruang lingkup yang ditandai oleh atribut ," mungkin diklarifikasi dengan "kecuali ruang lingkup dalamnya ditandai berbeda, jika ada memang lingkup batin seperti itu,” baik itu perlengkapan atau perakitan. Hanya pendapat saya, tetapi dua atribut memang terlihat agak membingungkan. Mungkin itu hanya cara berpikir saya, saya benar-benar tidak tahu.

dua implementasi terpisah

Dalam gedankenworld implementasi unicorn dan pelangi tidak mempengaruhi sisi yang menghadap pengguna. Tapi kami (baik Anda perancang NUnit dan saya salah satu penggunanya) semuanya adalah insinyur, dan teknik adalah tentang kompromi. Jika menggunakan satu atribut untuk cakupan yang berbeda pada kenyataannya akan menyebabkan kemacetan internal dan beban pemeliharaan, saya tidak akan mengeluh sama sekali. :)

Pada akhirnya, bagi saya ini adalah masalah kecil, dan saya tidak merasa kuat untuk atribut yang sama dan dapat hidup dengan atribut yang diatur dengan cara apa pun.

@kkm000 Ya, saya bermaksud "sekali per kasus uji."

Saya setuju bahwa semuanya masuk akal jika Anda menganggapnya sebagai serangkaian cakupan bersarang. Namun, pengalaman menunjukkan bahwa pengguna (a) tidak selalu berpikir seperti itu atau (b) tidak mendapatkan definisi ruang lingkup yang benar.

Sebagai contoh yang terakhir, pertimbangkan kelas bersarang dan kelas dasar. Tak satu pun dari mereka diperlakukan oleh NUnit sebagai "cakupan" bersarang untuk tujuan pengujian tetapi mereka mungkin tampak seperti itu bagi pengguna.

Saya akan mengakui bahwa masalah yang saya sebutkan dengan Timeout / DefaultTimeout sedikit lebih serius, karena Timeout pada fixture mungkin dengan mudah berarti bahwa timeout diterapkan ke seluruh fixture, yang tentu saja tidak. Dalam hal ini, Timeout pada perakitan mungkin berlaku untuk seluruh perakitan, yang tidak.

Dalam hal ini, tampaknya ada lebih sedikit ambiguitas, karena kami menggunakan "per test case" dalam nama sehingga saya dapat dengan mudah memilih keduanya. Lagipula aku tidak sedang mengerjakannya. :senyum:

Kalau dipikir-pikir, jika Timeout disebut TestCaseTimeout itu akan lebih baik!

Saya senang dengan satu [FixtureInstancePerTestCase] . Tampaknya beban mental paling sedikit pada pengguna.

@nunit/framework-team Bisakah Anda membantu kami menentukan arah, membaca sekilas dari https://github.com/nunit/nunit/issues/2574#issuecomment -426347735 ke bawah?

[FixtureInstancePerTestCase] juga merupakan opsi yang paling tidak saya sukai. Kurangnya ekstensibilitas adalah satu-satunya downside.

Saya tidak suka masalah [FixtureOptions] - saya pikir itu terlalu membingungkan. Saya pribadi lebih suka satu atribut untuk Majelis dan kelas - saya menghargai itu menyebabkan kami masalah sebelumnya untuk Timeout dan Paralellizable - namun ketika yang ini memiliki "Fixture" dalam namanya, tampaknya kurang ambigu bagi saya.

Saya pribadi mengutip menyukai opsi enum ... kecuali saya tidak dapat memikirkan bagaimana menamakannya dengan tepat untuk membuatnya lebih ringkas!

Apakah ada pembaruan tentang ini?

Setiap pembaruan akan diposting untuk masalah ini.

Seseorang dapat menyelesaikan masalah ini silahkan??
Saat ini kita harus membungkus semua kode pengujian kita seperti ini:
csharp [Test] public void TestExample(){ using(var tc = new MyTestContext()){ //code goes here } }
Dengan menggunakan fitur ini, kita dapat menginisialisasi konteks pengujian saat disiapkan dan dibuang saat dibongkar dan tetap aman untuk thread.

Hai, kami juga mengalami masalah ini di perusahaan kami. Kami bermigrasi dari kerangka kerja yang mendukung fitur ini (Py.Test), jadi ini sedikit membingungkan. Kami akan sangat menghargai jika Anda dapat menunjukkan kepada kami kode sumber yang mengontrol instantiasi ini sehingga kami dapat melakukan fork dan membuat perubahan yang diperlukan.

@MChiciak
Jika masalah ini tidak segera diperbaiki, kami sedang mempertimbangkan migrasi ke xUnit, karena di xUnit ini adalah perilaku default: "xUnit.net membuat instance baru dari kelas pengujian untuk setiap pengujian yang dijalankan" https://xunit .github.io/docs/shared-context

@LirazShay , @MChiciak Saya juga mengalami masalah ini tetapi setuju dengan poin @CharliePoole . Jadi saya membuat bidang khusus pengujian saya yang diinisialisasi di SetUp [ThreadStatic] dan berfungsi dengan baik.

@dfal
Terima kasih atas solusi yang bagus!

Saya akan menyukai ini. Saat ini saya memiliki rangkaian pengujian yang ada dengan ribuan pengujian yang berjalan di nunit, yang tidak dapat diparalelkan dengan mudah karena status bersama. Jika opsi ini disediakan, kami dapat memparalelkan rangkaian pengujian kami dengan lebih mudah.

Sebagai eksperimen pemikiran, bayangkan kita hanya mengubah cara kerja TestFixture. Apa yang akan pecah ini?

  1. Setiap pengujian yang menggunakan pengurutan dan bergantung pada perubahan status yang dibuat oleh satu pengujian untuk memengaruhi pengujian lainnya. Ini adalah praktik yang sangat buruk dalam kasus (seharusnya) unit test independen. Kami selalu memperingatkan orang untuk tidak melakukan ini dan saya tidak keberatan melanggar tes apa pun (dengan pesan, tentu saja) yang mengandalkan satu contoh dengan cara ini.

  2. Bergantung pada apa yang dilakukan dalam penyiapan satu kali, ada kemungkinan bahwa masing-masing instance perlengkapan tidak dapat berjalan secara paralel. Ini ironis, karena motivasi perubahannya adalah untuk memungkinkan pertandingan berjalan secara paralel! Ini bukan perubahan besar bagi orang-orang yang membutuhkan fitur tersebut, karena mereka sudah tidak dapat berjalan secara paralel, tetapi ini merupakan perubahan besar bagi mereka yang sudah mengandalkan menjalankan tes secara paralel __after__ `OneTimeSetUp' yang tidak dapat diparalelkan.

  3. OneTimeSetUp tingkat perlengkapan sekarang akan dijalankan beberapa kali. Ini akan menghilangkan keuntungan efisiensi dari pengaturan satu kali dan mungkin, dalam beberapa kasus, mengubah semantik. Secara khusus, jika inisialisasi mempengaruhi keadaan global dalam beberapa cara (misalnya menyiapkan database) mungkin menyebabkan kesalahan. Yang ini sepertinya kerusakan yang tidak dapat diterima bagi saya.

Saya menemukan eksperimen pemikiran ini berguna dalam memikirkan bagaimana menerapkan instansiasi perlengkapan per kasus uji. Ini jelas membutuhkan fitur non-default baru jika diperkenalkan di NUnit 3. Untuk NUnit 4 atau kerangka kerja baru lainnya (misalnya TestCentric) perilaku baru dapat dengan mudah menjadi default.

sebagai catatan, saya mengganti beberapa TestClass dan TestMethod dengan TestFixture dan Test masing-masing, dan menemukan bahwa NUnit dan MSTest memiliki semantik yang berbeda di sini. MSTest membuat instance baru untuk setiap pengujian di mana, karena saya yakin semua orang yang membaca ini mengetahuinya, NUnit menggunakan kembali instance TestFixture.

Alangkah baiknya jika saya bisa menulis TestFixture(lifeCycle = OneTestPerInstance)] atau yang serupa, untuk memudahkan migrasi ini.

_edit:_ Sebenarnya, baru saja melakukan porting tes untuk menggunakan metode Setup , ternyata kode penginisialisasi/sebelum/teardown kode yang ada cukup tersiksa di bawah MSTest, dan sekarang merupakan konstruktor penginisialisasi properti sederhana, yang jauh lebih bagus dan lebih lengkap, jadi saya senang saya menerjemahkannya. Namun, sebagai masalah kompatibilitas, akan menyenangkan untuk memilikinya.

@CharliePoole

Setiap pengujian yang menggunakan pengurutan dan bergantung pada perubahan status yang dibuat oleh satu pengujian untuk memengaruhi pengujian lainnya.

Aah, ya, istirahat, hancurkan tes ini, tolong cantik!!! Itu adalah penyalahgunaan terburuk dari kerangka kerja pengujian unit yang dapat saya pikirkan.

Setiap tingkat perlengkapan OneTimeSetUp

Uh, saya selalu berpikir ini dijalankan sekali per proses... Saya selalu bertanya-tanya mengapa tidak statis.

Tapi ya, kinerja juga merupakan poin yang sangat valid.

Fitur ini telah didiskusikan selama lebih dari 2 tahun sekarang, saya tidak bermaksud menyinggung, tetapi apakah ada rencana konkret dan realistis untuk benar-benar menerapkannya dalam waktu terdekat?

Saya ingin tahu, apakah tim saya harus merencanakan migrasi ke kerangka kerja lain atau apakah ini akan diselesaikan dalam beberapa bulan mendatang, karena masalah ini merupakan penghalang bagi kami.

Ini adalah perubahan besar pada cara pengujian berjalan dan perlu mempertimbangkan dampak penuh agar tidak sepenuhnya merusak mereka yang bergantung pada fungsi saat ini.

Cara terbaik untuk "menyelesaikan" ini adalah dengan membuat utas kelas pengujian Anda aman. Sama seperti aplikasi multi-utas yang baik, keamanan utas harus menjadi yang terpenting.
Jika Anda menggunakan bidang contoh yang Anda siapkan dalam metode penyiapan dan kemudian digunakan di banyak metode pengujian, kelas Anda tidak aman untuk thread. itu bukan kelemahan NUnit. Anda telah menulis singleton yang tidak aman.

Dengan membuat subkelas di dalam TestFixture Anda yang menyimpan informasi kontekstual yang Anda buat sebagai bagian dari setiap pengujian, Anda kemudian memastikan bahwa TestFixture tunggal Anda adalah threadsafe...sehingga...Anda "menyelesaikan" masalah tersebut.
Jadi daripada menghabiskan waktu "bermigrasi ke kerangka kerja lain" mungkin saya menyarankan agar Anda menghabiskan waktu itu membuat singleton TestFixtures Anda threadsafe .... Anda mungkin akan menemukan itu menjadi latihan yang jauh lebih cepat.

Misalnya, ketika saya pertama kali menemukan fakta bahwa NUnit TestFixture adalah singleton, saya mengonversi 100+ perlengkapan pengujian saya dengan lebih dari 500 tes menjadi threadsafe (dan harus berpindah dari RhinoMocks ke Moq karena RhinoMocks juga tidak threadsafe) dan ini membutuhkan waktu saya sekitar 4 minggu (sejujurnya, Badak -> Moq mengambil sebagian besar waktu itu!)

Anda 100% benar pada prinsipnya, tetapi ketika Anda memiliki rangkaian pengujian ~7000+ pengujian unit yang sudah ada, menulis ulang saja agar aman untuk thread adalah investasi besar.

Tidak yakin apakah ini lebih merupakan investasi daripada pindah ke kerangka kerja lain (mungkin ada beberapa alat untuk mengotomatiskannya) tetapi saya tidak berpikir permintaan fitur ini pada prinsipnya harus dibuang, IMO

Saya tidak menganjurkan bahwa fitur ini tidak boleh dilihat.

Tetapi perubahan besar pada cara kerja nunit, menurut saya harus dipertimbangkan untuk kompatibilitas ke belakang (yang mungkin cukup sulit) atau versi utama baru (juga bukan tugas kecil)

Saya menyarankan agar membuat kode yang ditulis oleh pengguna nunit threadsafe untuk dijalankan di lingkungan multi-utas adalah bentuk tindakan yang lebih mudah. Anda bahkan mungkin bisa membuat skrip itu.

Saya bukan kontributor repo ini, jadi saya tidak memiliki konteks tentang bagaimana penerapannya. Ini mungkin sangat naif karena itu, tetapi bukankah mungkin untuk mengimplementasikan ini sebagai sakelar opsional? Sehingga ini tidak merusak apa-apa?

Seluruh masalah ini membahas berbagai cara agar sakelar opsional dapat diimplementasikan ... ada banyak cara

Ini adalah proyek open source yang dikelola sepenuhnya oleh sukarelawan. Setelah fitur diterima, seperti ini, menunggu seseorang di tim atau di luar untuk mengambilnya dan melakukannya. Tidak ada "bos" untuk menugaskan pekerjaan itu kepada siapa pun. Sebenarnya adalah hal yang baik bahwa tidak ada yang melakukan itu jika mereka tidak punya waktu untuk mencurahkannya untuk menyelesaikannya. Dengan cara itu tersedia bagi siapa saja yang mampu dan tertarik untuk melakukannya.

PR selalu diterima tetapi ini mungkin bukan kontribusi pertama kali bagi seseorang, karena berpotensi merusak banyak hal lain di NUnit. Pendapat saya sih.

Saya tidak lagi berkontribusi secara aktif seperti di masa lalu sehingga komentar saya tentang bagaimana saya pikir itu harus dilakukan diberikan sebagai saran untuk siapa pun yang mengambilnya.

Saya setuju bahwa itu sudah ada untuk sementara waktu dan saya ingin melihatnya dilakukan sendiri.

@BlythMeister Saya pikir sakelar sedikit menyederhanakannya - seperti yang mungkin Anda ketahui tetapi yang lain mungkin tidak.

Tentu, ini adalah sakelar, tetapi tidak hanya menyalakan bola lampu. Ini lebih seperti mengalihkan radio Anda dari AM ke FM ... sirkuit yang sama sekali baru ikut bermain.

Dalam hal ini, apa yang terlibat adalah siklus hidup baru dalam pelaksanaan tes, sehingga cukup banyak kode yang harus diganti. Bukan hal kecil. Risikonya adalah memastikan bahwa "sirkuit" baru tidak menimpa yang lama dengan cara apa pun.

Maaf benar, penjelasan saya tidak jelas mengapa "saklar" tidak sederhana!

@BlythMeister Saya benar-benar melakukan persis seperti yang Anda gambarkan - membuat objek sekali pakai untuk melacak status selama pengujian dan kemudian membuangnya setelah pengujian selesai - untuk menggerakkan harness uji Selenium saya. Salah satu masalah utama dengan pendekatan itu adalah bahwa negara tidak terus-menerus melakukan penghancuran. Akibatnya, tidak mungkin menjalankan tindakan secara kondisional untuk pengujian yang gagal (ambil tangkapan layar, log, atau bagian status lainnya) karena status pengujian tidak akan diselesaikan di dalam metode pengujian itu sendiri dan hanya dapat dievaluasi selama pembongkaran. Jadi, meskipun objek status adalah cara yang ampuh untuk membuat pengujian thread-safe, objek tersebut hanya berfungsi saat Anda tidak perlu melakukan sesuatu dengan hasil pengujian selama teardown.

Penting untuk diingat bahwa melakukan sesuatu dengan hasil tes bukanlah tujuan dari TearDown yang kami rancang. Tentu saja, saya tahu banyak orang menggunakannya seperti itu dan saya memahami motivasinya tetapi ada banyak kasus sudut rumit yang pada dasarnya bermuara pada fakta bahwa TearDown adalah bagian dari ujian dan itu belum berakhir sampai selesai.

Dengan NUnit 3 kami berharap dapat melihat lebih banyak pengguna yang memeriksa hasil di luar kerangka kerja menggunakan ekstensi mesin tetapi bekerja di dalam pengujian itu sendiri tampaknya lebih akrab dan dapat diakses.

@CharliePoole

  1. OneTimeSetUp tingkat perlengkapan sekarang akan dijalankan beberapa kali. Ini akan menghilangkan keuntungan efisiensi dari pengaturan satu kali dan mungkin, dalam beberapa kasus, mengubah semantik. Secara khusus, jika inisialisasi mempengaruhi keadaan global dalam beberapa cara (misalnya menyiapkan database) mungkin menyebabkan kesalahan. Yang ini sepertinya kerusakan yang tidak dapat diterima bagi saya.

Mengapa kami tidak dapat mengimplementasikan fitur Instance-per-test-case dan masih menjalankan OneTimeSetUp hanya sekali (tetap ingat utas utama untuk menyebutnya atau semacamnya)?

Karena itu akan merusak OneTimeSetUp yang ada yang menginisialisasi instance, yang sangat umum.

Saya melihat ini sebagai masalah dalam solusi berbasis sakelar apa pun. Ini bukan masalah jika kita mendefinisikan alternatif baru untuk menguji perlengkapan, pendekatan yang menurut saya cukup menggoda.

Karena itu akan merusak OneTimeSetUp yang ada yang menginisialisasi instance, yang sangat umum.

Bagaimana kalau hanya mengizinkan metode OneTimeSetUp dan OneTimeTearDown statis ketika fitur "instance per test case" diaktifkan? Itu akan memaksa anggota yang diinisialisasi dalam metode tersebut menjadi statis, sehingga jelas dibagikan di antara instance fixture.

Sementara motivasi utama dari masalah Github ini adalah paralelisasi, saya ingin memasukkan _prinsip yang paling tidak mengejutkan_ dan _menghindari kesalahan yang disebabkan oleh pengujian yang saling mempengaruhi_ sebagai motivasi juga. (Maaf jika saya melewatkan bahwa orang lain telah menyebutkan ini.)

Jika Anda tidak membaca kembali cukup jauh di utas yang agak lama ini, komentar saya tidak menentang fitur tersebut, tetapi menentang implementasi tertentu yang disarankan. Saya lebih suka mengganti TestFixture dengan jenis perlengkapan yang sama sekali baru, yang bekerja secara berbeda.

Saya pikir saya kepala sekolah yang paling tidak mengejutkan bekerja dengan baik di sini. Tidak mengherankan jika perlengkapan baru bekerja secara berbeda.

Jika, di sisi lain, kita membuat bendera yang mengontrol perlengkapan yang ada, saya dapat memikirkan lima atau enam tempat berbeda dalam implementasi saat ini di mana kita perlu bercabang pada bendera itu.

Itu tidak berarti bahwa kedua jenis perlengkapan mungkin tidak berbagi kode tertentu. Tapi itu di luar pandangan pengguna.

@fschmied BTW, jika kami melakukan kerangka kerja baru (atau mungkin bahkan NUnit 4) maka saya akan berpendapat berbeda ... Saya pikir kami seharusnya mempersulit pengujian untuk saling mengganggu di NUnit 2 dan 3. Namun , selama kami membuat perubahan di NUnit 3, saya pikir aturan kompatibilitas.

Saya lebih suka mengganti TestFixture dengan jenis perlengkapan yang sama sekali baru, yang bekerja secara berbeda.
[...]
Saya pikir saya kepala sekolah yang paling tidak mengejutkan bekerja dengan baik di sini. Tidak mengherankan jika perlengkapan baru bekerja secara berbeda.

Anda benar tentu saja. Yang saya maksud adalah bahwa untuk jenis perlengkapan tes asli, perilakunya masih akan mengejutkan bagi kebanyakan orang. (Kebetulan rekan kerja terkejut dua minggu lalu, ketika dia menemukan kasus tes yang memengaruhi tes lain. Dia telah menggunakan NUnit sejak 2013, itulah sebabnya saya mencari masalah ini.)

Tapi tentu saja, ini adalah tradeoff antara kompatibilitas mundur, kompleksitas implementasi, dan keinginan untuk memperbaiki dosa asal [1].

Kembali ke masalah OneTimeSetUp : Saya pikir itu masih penting untuk memiliki OneTimeSetUp per perlengkapan tes, bahkan jika kelas perlengkapan tes dipakai per tes. Untuk menghindari kebingungan jika OneTimeSetUp memanipulasi keadaan instance, saya akan mengusulkan untuk memberlakukan OneTimeSetUp (dan OneTimeTearDown ) menjadi statis dengan fitur instance-per-test. (Tidak masalah apakah itu diimplementasikan sebagai sakelar atau sebagai perlengkapan uji jenis baru.)


[1] James Newkirk menulis ini :

Saya pikir salah satu kesalahan terbesar yang dibuat ketika kami menulis NUnit V2.0 adalah tidak membuat instance baru dari kelas perlengkapan uji untuk setiap metode pengujian yang ada. Saya mengatakan "kami" tapi saya pikir ini salah saya. Saya tidak begitu memahami alasan di JUnit untuk membuat instance baru dari perlengkapan pengujian untuk setiap metode pengujian.

Dia juga menulis ini, meskipun:

[...] akan sulit untuk mengubah cara kerja NUnit sekarang, terlalu banyak orang akan mengeluh [...]

:)

Ya, Jim dan saya telah berdebat tentang yang satu ini selama bertahun-tahun. :smile: Satu hal yang kami sepakati adalah kalimat terakhir. Jim pergi untuk melakukan kerangka kerja baru sebelum menerapkan pendekatan pilihannya. Saya pada dasarnya melakukan hal yang sama dan SetUp/TearDown adalah satu hal yang mungkin saya lakukan secara berbeda.

Saya terus terkejut betapa banyak pengguna NUnit yang cukup berpengalaman tidak mengetahui penggunaan instance yang sama untuk semua kasus uji. Mungkin lebih banyak dikenal ketika NUnit pertama kali keluar karena jelas perbedaannya dengan JUnit. Nah, itu semua terlupakan dan orang-orang baru datang ke NUnit dengan harapan bahwa NUnit tidak cocok. Sesuatu untuk dipikirkan.

jadi .. apakah ini untuk diperebutkan? atau masih dalam tahap desain?

sekarang di .NET mendaratkan XUnit tanpa TestContext atau NUnit, dengan tes paralel boilerplate

Fitur ini diterima dan diberi prioritas normal. Tidak ada yang ditugaskan untuk itu dan itu tidak terdaftar pada tonggak sejarah. Itu berarti seseorang harus memutuskan bahwa mereka ingin mengerjakannya. Itu bisa menjadi commiter atau kontributor.

Kami biasanya tidak melakukan fase desain - jika kami menginginkannya, label desain diterapkan - tetapi meskipun demikian, siapa pun yang melakukan ini disarankan untuk mengirim komentar yang menjelaskan bagaimana mereka berencana untuk mengimplementasikannya, terutama bagaimana tampilannya. pengguna. Jika Anda memulai dengan mengkodekannya, Anda mungkin akan berakhir dalam diskusi desain, jadi mungkin yang terbaik adalah menyelesaikannya terlebih dahulu.

Jadi, apakah Anda ingin mengerjakannya?

tentu, saya baru saja mulai bekerja di
https://github.com/avilv/nunit/tree/instance-per-test

saya baru mengenal basis kode ini jadi saya mencoba untuk tetap menggunakan gaya kode asli sebanyak yang saya bisa
sejauh ini saya memperkenalkan atribut InstancePerTestCase dan berencana untuk mendukungnya di tingkat perakitan dan kelas

beri tahu saya jika saya jauh dari pangkalan di sini

Saya menambahkan komentar pada beberapa detail implementasi pada komit terakhir Anda.

Sejauh desain, saya pikir atribut yang berasal dari IApplyToContext masuk akal. Saya pikir saya lebih suka sesuatu yang lebih umum daripada hanya InstancePerTestCase, mungkin

C# [FixtureLifeCycle(LifeCycle.InstancePerTestCase]

Itu akan memungkinkan Anda untuk mengaturnya di tingkat perakitan dan mengatur ulang pada perlengkapan individu.

CharliePoole Anda terlalu banyak, terima kasih banyak atas umpan balik dan bantuannya

saya pikir saya mengerti bagaimana hal-hal dibangun / apa yang dilakukan apa, dll. perlahan tapi pasti

inilah proposal saya untuk enum LifeCycle (saya juga memperbarui cabang saya dengan saran Anda)

    /// <summary>
    /// Specifies the lifecycle for a fixture.
    /// </summary>
    public enum LifeCycle
    {
        /// <summary>
        /// A single instance is created and for all test cases.
        /// </summary>
        SingleInstance,

        /// <summary>
        /// A new instance is created for each test case for fixtures that are marked with <see cref="ParallelizableAttribute"/>.
        /// </summary>
        InstancePerTestCaseForParallelFixtures,

        /// <summary>
        /// A new instance is created for each test case.
        /// </summary>
        InstancePerTestCase
    }

@jnm2 Apa pendapat Anda tentang pendekatan ini - Anda adalah semacam "sponsor" dari fitur ini sejak awal. Saya agak tidak yakin tentang InstancePerTestCaseForParallelFixtures , yang mungkin terlalu berlebihan untuk implementasi awal. Seperti biasa, setelah diimplementasikan, kami terjebak dengannya.

Jika kita __melakukan__ menerima ikatan dengan paralelisme, itu harus berbasis konteks, bukan berbasis atribut. Artinya, tes apa pun dapat dijalankan secara paralel, jadi ini lebih seperti `InstancePerTestCaseWhenCasesAreParallel. Perlengkapan paralel tidak terlalu membutuhkan fitur ini. Dan mungkin lebih baik diserahkan kepada pengguna untuk memutuskan.

Saya juga setuju bahwa itu tidak boleh digabungkan dengan cara apa pun dengan paralelisme. paralelisme adalah alasan untuk menambahkan fitur ini tetapi setelah kami setuju bahwa fitur ini berguna, menurut saya, itu tidak harus tercermin dalam kode

saya setuju, saya mengerti kasus penggunaan untuk menggunakannya hanya dalam skenario paralel karena di situlah model default NUnit saat ini akan menggigit Anda, tetapi mudah-mudahan ini akan menjadi siklus hidup default di masa depan (nunit 4?) sudah seperti ini dari awal

mungkin kita harus meninggalkannya di SingleInsance/InstancePerTestCase

pertanyaan lain adalah haruskah kita menangani pola IDisposable di sini, yang akan membuat SetUp/TearDown berlebihan tetapi akan terasa jauh lebih alami ala gaya XUnit.

Kami sudah membuang Perlengkapan Uji apa pun yang mengimplementasikan IDisposable. Anda hanya perlu memastikan bahwa itu terus berfungsi.

hebat, akan dilakukan.

sakit mungkin mulai menambahkan kasus uji hari ini, tetapi karena ini adalah mekanisme inti, itu mungkin menambahkan sedikit kasus yang hanya memastikan hal yang sama berfungsi ketika InstancePerTestCase aktif, perbaiki saya jika saya salah

DisposeFixtureCommand menangani pemanggilan Dispose.

IMO masalah utama Anda adalah memastikan bahwa seluruh aliran perintah untuk perlengkapan pengujian dipanggil __untuk setiap pengujian__. Perubahan yang saya lihat sejauh ini dengan benar membuat aliran perintah yang tampaknya melakukan pekerjaan itu, tetapi pertanyaan kuncinya adalah bagaimana memastikan bahwa itu dipanggil untuk setiap kasus uji. (Perhatikan bahwa struktur perintah untuk perlengkapan berbeda dari untuk kasus uji.) Saya pikir bahwa compositeworkitem harus peka terhadap konteks dan menjalankan perintah dengan benar (seperti yang telah Anda lakukan) tetapi saya tidak yakin itu akan mendapatkan kesempatan melakukannya untuk __every__ test case. (Saya hanya membaca kodenya, jadi saya bisa saja salah.)

@CharliePoole sepertinya Anda benar, saya membuat grafik kecil tentang apa yang sebenarnya menjalankan metode pengujian:
image

saat ini saya membuat instance objek uji di loop RunChildren, tetapi itu tidak akan cukup untuk perintah Coba Ulang/Ulangi, belum lagi jika ada pembungkus lain yang saya lewatkan / seseorang membutuhkan yang baru.
itu harus lebih dekat dengan TestMethodCommand

saya memperbarui perubahan terbaru di
https://github.com/avilv/nunit/tree/instance-per-test-2

menghapus InstancePerTestCaseWhenCasesAreParallel
mengimplementasikan FixtureLifeCycle dengan menambahkan fixture Construct/Dispose di dalam MakeTestCommand di SimpleWorkItem , dan melewatkannya di CompositeWorkItem MakeOneTimeSetUpCommand

tampaknya berfungsi dengan baik, juga lebih masuk akal dengan cara kerangka dibangun
sekarang saya hanya perlu menambahkan banyak tes unit untuk memastikan semuanya berfungsi dengan benar

ada kemungkinan ini akan ditinjau sebelum versi berikutnya? saya masih siap untuk perbaikan / perubahan tentu saja

Pendekatan ini terdengar bagus bagi saya dan saya ingin menggunakannya di rilis berikutnya jika kita bisa melakukannya!

ada kemungkinan kita bisa meninjau ini? rasanya seperti benar-benar dekat

Penasaran apakah ada pergerakan pada fitur ini? Sepertinya @avilv sudah cukup dekat. Sudah lama menantikan fitur seperti ini, dan bersemangat untuk menggunakannya!

Terima kasih semuanya!

Apakah fitur ini berfungsi sekarang?

@CharliePoole bisakah Anda meninjau @avilv versi terbaru? Tampaknya diterapkan sepenuhnya. Saya pikir banyak orang menunggu fitur ini.

@janissimsons Maaf, tapi saya tidak lagi dalam proyek ini sehingga ulasan saya tidak akan memajukannya. @nunit-framework-team Tidak bisakah seseorang mengulas ini?

Hargai semua pekerjaan yang telah dilakukan untuk menyatukan #3427. @rprouse ada ide kapan kita bisa mengharapkan ini dalam rilis?

Bagaimana cara kami menggunakan fitur baru? Bisakah seseorang menulis deskripsi singkat tentang atribut baru dan cara menggunakannya dengan benar?

Saya berencana untuk merilis dalam beberapa minggu ke depan. Aku ingin menunggu sampai. NET 5 sudah final dan lakukan sedikit pengujian akhir dengannya sebelum dirilis. Saya ragu akan ada masalah, tetapi itu sangat dekat sehingga saya pikir akan lebih baik untuk menunggu.

@rprouse Adakah perkiraan kapan Anda akan merilis versi baru? Saya ingin mulai menggunakan fitur ini.

@janissimsons segera. Kami sedang mengatasi beberapa masalah dengan build .NET 5.0 kami dan beberapa masalah lainnya, lalu saya akan merilisnya. Anda dapat melacak status di papan proyek ini, https://github.com/nunit/nunit/projects/4

Ini adalah berita yang luar biasa!

Apakah saya mengerti dengan benar bahwa ini akan memungkinkan hal berikut di NUnit?

Gabungkan semua ini:

  • Tes diparameterisasi
  • Jalankan tes secara paralel.
  • Mampu menjalankan variasi parameter dari tes yang sama secara paralel.
  • Tes dapat didorong oleh data

Mencoba beberapa hal di masa lalu di sini https://github.com/jawn/dotnet-parallel-parameterized-tests

Apakah rilis baru akan tetap kompatibel dengan .NET Full Framework 4.7.2 atau setidaknya 4.8?

Salam,
DR

@jawn Saya percaya semua itu akan berhasil, ya. Saya telah menguji semua hal itu, tetapi NUnit memiliki banyak fitur sehingga mungkin ada kasus tepi dengan beberapa kombinasi fitur. Jangan ragu untuk menarik paket NuGet malam kami dan mencobanya! Kami ingin lebih banyak pengujian dan umpan balik sebelum kami rilis.

@drauch ya, rilis 3.13 masih akan mendukung kembali ke .NET 3.5.

@avilv Kerja bagus!!
omong-omong menggunakan alat apa yang Anda buat diagram alir yang bagus itu?
Terima kasih

@rprouse Bisakah Anda juga memperbarui dokumentasi tentang cara menggunakan fitur baru ini? Terima kasih!

@janissimsons Saya berencana memperbarui dokumen untuk rilis. Versi singkatnya adalah menambahkan [FixtureLifeCycle(LifeCycle.InstancePerTestCase)] ke kelas pengujian Anda, mungkin bersama dengan [Parallelizable(ParallelScope.All)] dan turunan dari kelas pengujian Anda akan dipakai untuk setiap pengujian yang dijalankan di dalam kelas. Melakukan hal itu memungkinkan Anda untuk menggunakan variabel anggota di kelas dalam pengujian paralel tanpa khawatir tentang pengujian lain yang mengubah variabel.

@LirazShay Diagram di tangkapan layar dibuat di Visual Studio Enterprise. Anda dapat mengklik kanan pilihan di Solution Explorer dan memilih "Show on Code Map," misalnya. https://docs.microsoft.com/en-us/visualstudio/modeling/map-dependencies-across-your-solutions ReSharper memiliki fitur serupa.

@jnm2 Terima kasih banyak!!!
Saya memiliki VS enterprise dan tidak tahu tentang fitur ini yang bisa sangat membantu
Terima kasih banyak atas tipnya

Apakah saya benar bahwa ini telah dirilis dan merupakan bagian dari rilis 3.13?

Apakah saya benar bahwa ini telah dirilis dan merupakan bagian dari rilis 3.13?

Saya percaya ada beberapa masalah yang diamati dan diperbaiki, terutama mengenai aplikasi atribut tingkat perakitan di #3720 dan kami sedang menunggu 3.13.1 sekarang..

NUnit 3.13.1 telah dirilis.
Ada lagi masalah yang tersisa?

Tidak, itu sebabnya ditutup @andrewlaser

Apakah halaman ini membantu?
0 / 5 - 0 peringkat