Autofixture: Permintaan: Tambahkan Metode Klon ke IFixture

Dibuat pada 24 Sep 2020  ·  9Komentar  ·  Sumber: AutoFixture/AutoFixture

pengantar

Saya ingin jika IFixture memiliki metode Clone atau yang serupa untuk membuat salinan perlengkapan.

Saat ini saya dapat membuat metode ekstensi untuk melakukan ini:

public static IFixture Clone( this IFixture fixture)
{
   var cloneFixture = new Fixture();

   cloneFixture.Behaviors.Clear();
   foreach ( var behavior in fixture.Behaviors)
   {
      cloneFixture.Behaviors.Add( behavior );
   }

   cloneFixture.Customizations.Clear();
   foreach ( var customization in fixture.Customizations )
   {
      cloneFixture.Customizations.Add( customization );
   }

   cloneFixture.OmitAutoProperties = fixture.OmitAutoProperties;
   cloneFixture.RepeatCount = fixture.RepeatCount;

   cloneFixture.ResidueCollectors.Clear();
   foreach ( var residueCollector in fixture.ResidueCollectors )
   {
      cloneFixture.ResidueCollectors.Add( residueCollector );
   }

   return cloneFixture;
}

Tetapi saya merasa kemampuan untuk membuat salinan seperti itu harus ada dalam proyek itu sendiri.

rincian

Skenario saya adalah saya membuat beberapa instance kelas dengan banyak parameter dalam pengujian. Selain itu saya ingin memiliki parameter konstruktor menjadi nilai tertentu untuk satu instance dan nilai konstruktor yang berbeda untuk instance yang berbeda. Ada beberapa cara saya bisa melakukan ini; cara yang saya lakukan adalah:

fixture.Customizations.Add( new FilteringSpecimenBuilder(
            new FixedBuilder( mediaId ),
            new ParameterSpecification( 
                typeof( ObjectId ), 
                "mediaId" ) ) );
var mediaVM = fixture.Build<MediaViewModel>()   
                     .With( mvm => mvm.ParentMixerId, mixerId )
                     .With( mvm => mvm.TrackId, trackId )
                     .Create();

_ = fixture.Customizations.Remove( fixture.Customizations.Last() );

//...

Alasan penghapusan kustomisasi adalah saya mencoba tanpa berpikir mungkin kustomisasi terakhir yang ditambahkan akan memiliki prioritas lebih tinggi dan digunakan; tapi itu tidak terjadi.

Jika saya mencoba menyederhanakan ini dengan semacam metode ekstensi seperti:

public static IFixture AddCustomization<T>( this IFixture fixture, T value, string name )
{
   fixture.Customizations.Add( new FilteringSpecimenBuilder(
         new FixedBuilder( value ),
         new ParameterSpecification(
            typeof( T ),
            name ) ) );
   return fixture;
}

Ini mungkin berfungsi di beberapa tempat tetapi tidak di tempat lain karena setelah objek dibuat, perlengkapan berisi penyesuaian yang tidak lagi saya inginkan dan harus saya hapus.

Saya lebih suka tidak memiliki garis untuk menghapus penyesuaian yang ditambahkan. Jadi jika sebagai gantinya metode ekstensi saya membuat salinan perlengkapan saya yang saya tambahkan modifikasi pada salinannya, maka kreasi saya berfungsi seperti yang diharapkan dan perlengkapan asli tidak tersentuh.

Juga akan bekerja adalah kemampuan untuk menambahkan kustomisasi yang secara otomatis dihapus setelah objek dibuat.

Semoga ini semua masuk akal.

Terima kasih telah mempertimbangkan masalah saya :smiley:

feature request

Komentar yang paling membantu

@ajorians Saya akan menutup masalah karena tidak aktif tetapi saya tidak bisa berjalan dengan konstruktor raksasa itu.
Saya harus bertanya, apakah Anda mempertimbangkan untuk mengubah desain Anda?

Dengan melihat konstruktor Anda, saya dapat melihat beberapa masalah dan solusi potensial untuk masalah Anda.

Pertama-tama Anda mencampur konsep entitas baru dan yang dapat disuntikkan.
Solusi paling mudah adalah memindahkan dependensi yang dapat disuntikkan (alias Antarmuka Layanan) ke properti dan menyuntikkannya menggunakan injeksi properti.
Ini akan memecahkan masalah langsung Anda karena harus menyalin Fixture dan akan membuat konstruktor lebih mudah dikelola.

Kedua adalah mengatasi bau kode injeksi berlebihan .
Ini berarti memindahkan layanan bersama yang paling umum digunakan ke dalam abstraksi terpisah.
Ini akan membantu desain Anda menghormati SRP dan dengan perluasan itu akan mengurangi jumlah besar pengaturan yang harus Anda lakukan saat ini, dalam pengujian Anda.

Karena kelasnya disebut ViewModel saya berasumsi Anda memiliki aplikasi MVVM. Kemungkinan Anda dapat memisahkan aplikasi Anda lebih jauh dengan memperkenalkan model perpesanan. Mungkin agregator acara. Sebagian besar kerangka kerja MVVM memilikinya sehingga Anda tidak perlu melakukan banyak pekerjaan penyiapan, untuk memanfaatkannya.

Beri tahu saya jika ini membantu.

Semua 9 komentar

@ajorians Selamat siang! Dari cara saya memahami contoh Anda, sepertinya kami sudah memiliki apa yang Anda butuhkan. Metode fixture Build() sebenarnya membuat salinan grafik internal yang tidak dapat diubah, sehingga Anda dapat menerapkan penyesuaian "satu kali" di atas AutoFixture tanpa mempertahankannya.

Tinjau demo taman bermain ini:

```c#
Model kelas publik
{
publik int Id { dapatkan; mengatur; }
Nama string publik { dapatkan; mengatur; }
}

[Fakta]
Demo kekosongan publik()
{
var perlengkapan = Perlengkapan baru();
perlengkapan. Sesuaikan(c => c.Dengan(m => m.Id, 42));

var value1 = fixture.Create<Model>();
Assert.Equal(42, value1.Id);
Assert.NotNull(value1.Name);

var value2 = fixture.Build<Model>()
    .With(m => m.Id, (int genInt) => genInt * 2 /* make it always even */)
    .Without(x => x.Name)
    .Create();
Assert.NotEqual(42, value2.Id);
Assert.Equal(value2.Id % 2, 0);
Assert.Null(value2.Name);

var value3 = fixture.Create<Model>();
Assert.Equal(42, value3.Id);
Assert.NotNull(value3.Name);

}
```

Beri tahu kami apakah itu menyelesaikan kebutuhan Anda. Jika tidak, jelaskan lebih spesifik mengapa itu tidak berhasil untuk Anda, sehingga kami dapat mencoba membantu.

Terima kasih!

Hai @zvirja ,

Selamat siang!

Jika saya boleh; kelas Model tidak akan memiliki set dan hanya dapat disetel melalui konstruktor. Jadi jika Anda menggantinya dengan ini:

public class Model
{
   public Model( int id, string name )
   {
      Id = id;
      Name = name;
   }

   public int Id { get; }
   public string Name { get; }
}

Maaf untuk tidak menentukan bahwa awalnya.

Jadi seperti yang saya pahami, saya harus menambahkan penyesuaian untuk melakukan parameter konstruktor. Dan penyesuaian ini sebelum Build() .

Jika tertarik, beberapa alasan kami melakukan banyak hal dalam konstruktor dan tanpa penyetel adalah agar kami dapat menggunakan kata kunci readonly dan memastikan bahwa variabel anggota disetel dan tidak dapat di-unset pada saat suatu metode ditelepon.

Harapan yang lebih masuk akal. Terima kasih telah membantu dengan ini! :senyum:

@ajorians Apakah saya memahami masalah Anda dengan benar, bahwa Anda memiliki model dengan argumen input dalam jumlah besar, jadi Anda ingin menyesuaikan hanya salah satunya, tanpa harus secara eksplisit mengkhawatirkannya. Benar?

Jika tidak, Anda selalu dapat menulis sesuatu seperti
c# var value2 = fixture.Build<Model>() .FromFactory((int id, string name) => new Model(id, name)) .Create();

Sayangnya, skalanya tidak cukup bagus dengan bertambahnya jumlah parameter

@ajorians Apakah saya memahami masalah Anda dengan benar, bahwa Anda memiliki model dengan argumen input dalam jumlah besar, jadi Anda ingin menyesuaikan hanya salah satunya, tanpa harus secara eksplisit mengkhawatirkannya. Benar?

Ya, itu benar sekali.

Sayangnya, skalanya tidak cukup bagus dengan bertambahnya jumlah parameter

Untungnya Freeze berfungsi untuk banyak kelas kami dengan jumlah parameter yang terus bertambah. Tapi ya kelas yang saya pikirkan di mana saya menggunakan penyesuaian untuk melakukan parameter konstruktor saat ini memiliki lebih dari parameter 30 :
image

Dan mungkin akan menambah 4 atau lebih sebelum akhir tahun ini.

Saya mendapatkan ini mungkin tidak biasa.

Tapi ya, saya memiliki banyak argumen konstruktor input yang berkembang dan saya ingin menyesuaikan hanya satu (atau beberapa) dari mereka tanpa memikirkan sisanya. Tanpa jenis itu terdaftar atau dibekukan untuk seluruh tes. Dan secara ringkas menambahkan penyesuaian adalah sekitar 5 baris kode.

Saya beruntung bisa melakukan semua itu; tetapi cara saya melakukannya melibatkan pembuatan metode ekstensi clone dalam kode sisi klien saya.

Semoga ini semua masuk akal. Terima kasih telah melihat ini bersama saya :smiley:

@ajorians Saya akan menutup masalah karena tidak aktif tetapi saya tidak bisa berjalan dengan konstruktor raksasa itu.
Saya harus bertanya, apakah Anda mempertimbangkan untuk mengubah desain Anda?

Dengan melihat konstruktor Anda, saya dapat melihat beberapa masalah dan solusi potensial untuk masalah Anda.

Pertama-tama Anda mencampur konsep entitas baru dan yang dapat disuntikkan.
Solusi paling mudah adalah memindahkan dependensi yang dapat disuntikkan (alias Antarmuka Layanan) ke properti dan menyuntikkannya menggunakan injeksi properti.
Ini akan memecahkan masalah langsung Anda karena harus menyalin Fixture dan akan membuat konstruktor lebih mudah dikelola.

Kedua adalah mengatasi bau kode injeksi berlebihan .
Ini berarti memindahkan layanan bersama yang paling umum digunakan ke dalam abstraksi terpisah.
Ini akan membantu desain Anda menghormati SRP dan dengan perluasan itu akan mengurangi jumlah besar pengaturan yang harus Anda lakukan saat ini, dalam pengujian Anda.

Karena kelasnya disebut ViewModel saya berasumsi Anda memiliki aplikasi MVVM. Kemungkinan Anda dapat memisahkan aplikasi Anda lebih jauh dengan memperkenalkan model perpesanan. Mungkin agregator acara. Sebagian besar kerangka kerja MVVM memilikinya sehingga Anda tidak perlu melakukan banyak pekerjaan penyiapan, untuk memanfaatkannya.

Beri tahu saya jika ini membantu.

Hai @aivascu ,

@ajorians Saya akan menutup masalah karena tidak aktif

Jika ada yang bisa saya lakukan, beri tahu saya. Menjadi permintaan fitur yang bisa saya lihat sampai diimplementasikan mungkin memiliki cukup banyak ketidakaktifan.

Saya harus bertanya, apakah Anda mempertimbangkan untuk mengubah desain Anda?

Saat ini kami sedang mempertimbangkan untuk menjadi aplikasi prisma . Itu akan membantu dengan beberapa item yang Anda sebutkan. Tapi sayangnya saya tidak melihat kami mengubah filosofi hanya meletakkan segala sesuatu di konstruktor sehingga dapat diperiksa untuk null sekali dan menjadi readonly dan selalu menjadi non- null melalui masa pakai kelas. Kita perlu melakukan sesuatu. Tapi saya tidak berpikir ini akan membaik di tahun depan atau lebih.

Beri tahu saya jika ini membantu.

Semua ini memang membantu. Saya akan memberi tahu tim bahwa bahkan orang-orang di dunia berpikir kita perlu mempertimbangkan untuk membuat perubahan.

Tetapi jika saya boleh bertanya apakah masuk akal untuk IFixture memiliki Clone atau metode serupa? Atau entah bagaimana mengizinkan orang untuk menambahkan penyesuaian dan kemudian pop penyesuaian terakhir yang ditambahkan dimatikan atau entah bagaimana kembali ke keadaan sebelum menambahkan penyesuaian? Saya setuju bahwa memahami skenario yang mendorong perilaku yang diinginkan ini penting dan tentu saja skenario memiliki banyak masalah. Dan saya melihat Anda tidak ingin menambahkan kerumitan tambahan kecuali ada kasus penggunaan yang baik. Anda dapat memilih apa yang terjadi selanjutnya, tetapi jika saya dapat membantu, beri tahu saya.

Terima kasih telah membaca dan mempertimbangkan :smiley:

@ajorians pendapat saya adalah bahwa kita tidak boleh menambahkan metode .Clone() ke kelas Fixture . Seperti yang saya lihat seharusnya hanya ada satu perlengkapan tes pada waktu tertentu untuk setiap tes tunggal.

Ada beberapa permintaan untuk mengimplementasikan fitur unfreeze/eject/freeze-once di masa lalu tetapi tidak ada yang mengimplementasikannya.
Saya pikir alasannya adalah bahwa tidak ada cara sepele untuk mengidentifikasi dengan andal pembuat mana yang akan mengembalikan nilai beku atau apa nilai yang disuntikkan terakhir.

Yang dapat Anda lakukan adalah menerapkan relai yang mengabaikan permintaan setelah hitungan tertentu dari resolusi yang berhasil, seperti di bawah ini. Anda juga dapat memperbarui implementasi untuk melewati sejumlah permintaan tertentu. Sisanya harus dicakup oleh penyesuaian yang Anda gunakan di pos asli.

public class CountingRelay : ISpecimenBuilder
{
    public CountingRelay(ISpecimenBuilder builder, int maxCount)
    {
        this.MaxCount = maxCount;
        this.Builder = builder;
    }

    public int Count { get; private set; }
    public int MaxCount { get; }
    public ISpecimenBuilder Builder { get; }

    public object Create(object request, ISpecimenContext context)
    {
        if (this.Count == this.MaxCount) return new NoSpecimen();
        var result = this.Builder.Create(request, context);
        if (!(result is NoSpecimen)) this.Count++;
        return result;
    }
}

Hai @aivascu ,

@ajorians pendapat saya adalah bahwa kita tidak boleh menambahkan metode .Clone() ke kelas Fixture .

OKE. Baik terima kasih untuk mempertimbangkan.

Apa yang dapat Anda lakukan adalah menerapkan relai yang mengabaikan permintaan setelah hitungan tertentu dari resolusi yang berhasil

Aku akan melihatnya.

Terima kasih lagi! :senyum:

@ajorians Saya telah membuat permintaan fitur yang lebih formal #1214 untuk pengaturan ulang kustomisasi eksplisit dan otomatis. Anda dapat melacak kemajuan fitur ini di sana.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

mjfreelancing picture mjfreelancing  ·  4Komentar

malylemire1 picture malylemire1  ·  7Komentar

Ridermansb picture Ridermansb  ·  4Komentar

ploeh picture ploeh  ·  3Komentar

josh-degraw picture josh-degraw  ·  4Komentar