Autofixture: AutoMoq ketergantungan nyata

Dibuat pada 1 Okt 2020  ·  15Komentar  ·  Sumber: AutoFixture/AutoFixture

Hai,

Saya mencoba menggunakan Moq dari ketergantungan konkret.

[Theory, AutoMoqData]
public async Task Can_register_a_single_service_successfully(
    [Frozen] Mock<IdentityServerOptions> identityServerOptions,
    ServiceDiscoveryUsingIdentityServer sut
)
{...}

Ini menimbulkan kesalahan di mana AutoFixture mencoba membuat instance nyata dari IdentityServerOptions untuk disuntikkan ke sut alih-alih menggunakan tiruan beku.
Jika saya menghapus sut dan secara manual membuat passing di identityServerOptions.Object maka semuanya berfungsi, jadi teori kerja saya adalah bahwa autofixture/automoq tidak mengambil sehingga moq beku dapat memenuhi ketergantungan dan mencoba membuat yang baru contoh.

Saya juga mencoba Frozen(Matching.DirectBaseType) karena proxy yang dihasilkan oleh moq mewarisi dari nilai yang diminta, tetapi masih belum berhasil.

Apakah ada cara untuk mengatasi ini karena saya tidak dapat mengubah IdentityServerOptions karena ini adalah pihak ke-3 dan memiliki lebih banyak variabel untuk disuntikkan ke sut saya, yang membuat solusinya agak berantakan ketika saya hanya perlu mengakses satu dari contoh saya .

Terima kasih
Euan

question

Komentar yang paling membantu

Akhirnya punya kesempatan untuk mengujinya dan itu berfungsi dengan baik.
Saya melakukan perbaikan kecil (saya tahu ini masih belum cukup kuat untuk semua penggunaan) sehingga Anda masih dapat meminta objek beton alih-alih pembungkus Mock.

public void Customize(IFixture fixture)
{
    Type? mockedType = (typeof(IMock<object>).IsAssignableFrom(ParameterInfo.ParameterType))
        ? ParameterInfo.ParameterType.GetGenericArguments()[0]
        : ParameterInfo.ParameterType;
    fixture.Customizations.Add(new MockRelay(new ExactTypeSpecification(mockedType)));
}

Mengizinkan

[Theory]
[AutoData]
public void DependencyShouldBeSameAsMockedObject(
    [Frozen][Mock] DependencyClass dependencyMock,
    SystemUnderTest sut)
{
    Assert.Same(dependencyMock, sut.Dependency);
}

Semua 15 komentar

Cukup bekukan IdentityServerOptions dan gunakan Mock.Get .

Periksa halaman buklet yang saya tulis untuk pengembang saya ini: https://docs.educationsmediagroup.com/unit-testing-csharp/autofixture/combining-autofixture-with-nunit-and-moq

Sayangnya itu tidak berfungsi karena IdentityServerOptions bukan kelas abstrak atau antarmuka sehingga AutoMoqCustomization tidak menghasilkan Moq untuk itu.

Untuk kelengkapan atributnya adalah:

public AutoMoqDataAttribute()
    : base(() => new Fixture().Customize(new AutoMoqCustomization()))
{ }

Perhatikan bahwa untuk antarmuka dan kelas abstrak tampaknya tidak masalah jika Anda membekukan pembungkus tiruan atau antarmuka dan kemudian mendapatkan tiruan atau menggunakan .Object dalam kasus pengujian Anda.

Saya percaya sepenuhnya benar bahwa kelas konkret dalam parameter akan memberi Anda salinan nyata, jika tidak, sut tidak akan berfungsi, tetapi jika Anda menentukan ketergantungan dengan pembungkus Mock untuk memaksa tiruan maka Mock<IInterface> berfungsi seperti yang Anda harapkan, tetapi Mock<Concrete> tidak.

Maka saya tidak mengerti masalah Anda. Mengapa Anda perlu menggunakan Moq sama sekali?

IdentityServerOptions bukan kelas saya, dan tidak dapat dipakai dengan mudah dari unit test jadi saya perlu Mengejeknya untuk masuk ke sut . Semua ejekan berfungsi dengan baik, tetapi tampaknya AutoFixture/AutoMoq tidak menangkap fakta bahwa Mockdapat digunakan sebagai pengganti ketergantungan IdentityServerOptions .

Solusi saat ini:

[Theory, AutoMoqData]
public async Task Can_register_a_single_service_successfully(
    [Frozen] ILogger<ServiceDiscoveryUsingIdentityServer> logger,
    [Frozen] IAuditor<ServiceDiscoveryUsingIdentityServer> auditor,
    [Frozen] Mock<IdentityServerOptions> identityServerOptions,
    ... the other dependencies.
)
{
   //Arrange
   var sut= new ServiceDiscoveryUsingIdentityServer(logger, auditor, identityServerOptions, identityService, authorizationService, grpcService);
}

Ini membuatnya sedikit berantakan karena harus membuat daftar semua dependensi di setiap pengujian ketika saya biasanya hanya membutuhkan tiruan identityServerOptions untuk sebagian besar kasus pengujian.

@Euan-McVie maaf atas balasan yang terlambat.
Saya berharap dapat menjelaskan masalah yang Anda alami dan mungkin solusi yang dapat membantu Anda.

Meskipun saya tidak ada saat AutoMoq dikembangkan, saya yakin perilaku ini memang disengaja.

Seperti yang mungkin Anda ketahui, AutoMoq dapat menyuntikkan tiruan dua cara baik sebagai tiruan dan sebagai tipe yang diejek.
Implementasi saat ini secara default mengolok-olok antarmuka atau tipe abstrak apa pun, mengingat tidak ada penyesuaian lain yang telah mencegat permintaan untuk tipe yang sama. Itulah mengapa ejekan otomatis terjadi untuk permintaan yang telah melewati seluruh saluran resolusi, tepat sebelum melontarkan pengecualian karena permintaan tidak dapat dipenuhi.

Jika kita memindahkan kustomisasi ejekan otomatis lebih dekat ke awal pipa, maka sebagian besar jenis akan diselesaikan sebagai tiruan. Ini tidak diinginkan karena dependensi jenisnya adalah null atau tiruan akan muncul pada pembuatan karena beberapa klausa penjaga mencegah pembuatan objek dalam keadaan tidak valid.

Sekarang mungkin tampak logis bahwa tiruan [Frozen] akan diselesaikan dengan benar, tetapi karena desain internal kami tidak dapat membedakan jenis mana yang dibekukan/disuntikkan karena kondisi di mana instance yang sama akan selalu dikembalikan terlalu rumit.
Permintaan konteks untuk tiruan dari tipe tiruan hampir selalu menghasilkan nilai, karena hampir semua jenis dapat diejek dan kita tidak akan tahu apakah permintaan dipenuhi oleh nilai beku.

Sebagai solusi untuk masalah Anda, saya sarankan untuk menyesuaikan tipe beton yang akan dibuat dari tiruan.

public class CustomCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<IdentityServerOptions>(
            x => x.FromFactory<IFixture>(
                f => f.Create<Mock<IdentityServerOptions>>().Object));
    }
}

Karena Anda sudah membekukan tiruan di parameter pengujian Anda ketika Anda memintanya di SUT Anda, Anda akan menerima contoh yang sama.

[Theory, AutoMoqData]
public async Task Can_register_a_single_service_successfully(
    [Frozen] Mock<IdentityServerOptions> identityServerOptions,
    ServiceDiscoveryUsingIdentityServer sut)

Saya akan membiarkan masalah ini terbuka sebentar, jika Anda memiliki pertanyaan lebih lanjut tentang topik ini.

Terima kasih atas tanggapannya.

Apakah akan berhasil jika saya membuat MockAttribute : CustomizeAttribute sehingga saya dapat melakukan sesuatu seperti:

[Theory, AutoMoqData]
public async Task Can_register_a_single_service_successfully(
    [Frozen, Mock(typeof(IdentityServerOptions))] IdentityServerOptions identityServerOptions,
    [Frozen] ILogger<ServiceDiscoveryUsingIdentityServer> logger,
    [Frozen] IAuditor<ServiceDiscoveryUsingIdentityServer> auditor,
    ServiceDiscoveryUsingIdentityServer sut)

Ini akan lebih disukai karena atribut AutoMoqData berfungsi untuk menghasilkan parameter lain sehingga saya dapat mengontrol tiruan tertentu dari objek konkret per pengujian daripada memiliki banyak atribut gaya AutoMoqWithMockXYZ untuk skenario lain.

Apakah ini mungkin berhasil (belum memiliki kesempatan untuk menguji saya sendiri), atau apakah itu tidak akan digabungkan dengan benar dengan Frozen dan mendasari AutoMoqData ?

public AutoMoqDataAttribute()
    : base(() => new Fixture().Customize(new AutoMoqCustomization()))
{ }

Terima kasih

@Euan-McVie Saya kira Anda memang bisa melakukan itu. Berikut adalah implementasi naif dari penyesuaian yang dapat mencapai ejekan khusus parameter

public class MockCustomization : ICustomization
{
    public MockCustomization(ParameterInfo parameterInfo)
    {
        ParameterInfo = parameterInfo;
    }

    public ParameterInfo ParameterInfo { get; }

    public void Customize(IFixture fixture)
    {
        var mockedType = ParameterInfo.ParameterType.GetGenericArguments()[0];
        fixture.Customizations.Add(new MockRelay(new ExactTypeSpecification(mockedType)));
    }
}

Anda kemudian dapat menentukan atribut yang menggunakannya

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
public class MockAttribute : Attribute, IParameterCustomizationSource
{
    public ICustomization GetCustomization(ParameterInfo parameter)
    {
        return new MockCustomization(parameter);
    }
}

dan akhirnya tesnya akan terlihat seperti ini

[Theory]
[AutoData]
public void DependencyShouldBeSameAsMockedObject(
    [Frozen][Mock] Mock<DependencyClass> dependencyMock,
    SystemUnderTest sut)
{
    Assert.Same(dependencyMock.Object, sut.Dependency);
}

Menutup masalah.
Pertanyaan itu sepertinya sudah terjawab.

Tampak hebat terima kasih.

@aivascu menurut Anda jenis di atas dapat dimasukkan ke dalam perpustakaan AutoMoq?

Mereka tampaknya cukup umum dan tujuan umum untuk mendapatkan kehormatan.

@Kralizek ya mungkin berguna untuk memilikinya di lib AutoMoq.
Saya akan menambahkan masalah ke backlog, dengan proposal untuk fitur ini.

Akhirnya punya kesempatan untuk mengujinya dan itu berfungsi dengan baik.
Saya melakukan perbaikan kecil (saya tahu ini masih belum cukup kuat untuk semua penggunaan) sehingga Anda masih dapat meminta objek beton alih-alih pembungkus Mock.

public void Customize(IFixture fixture)
{
    Type? mockedType = (typeof(IMock<object>).IsAssignableFrom(ParameterInfo.ParameterType))
        ? ParameterInfo.ParameterType.GetGenericArguments()[0]
        : ParameterInfo.ParameterType;
    fixture.Customizations.Add(new MockRelay(new ExactTypeSpecification(mockedType)));
}

Mengizinkan

[Theory]
[AutoData]
public void DependencyShouldBeSameAsMockedObject(
    [Frozen][Mock] DependencyClass dependencyMock,
    SystemUnderTest sut)
{
    Assert.Same(dependencyMock, sut.Dependency);
}

@Kralizek ya mungkin berguna untuk memilikinya di lib AutoMoq.
Saya akan menambahkan masalah ke backlog, dengan proposal untuk fitur ini.

Karena saya baru-baru ini menemukan masalah ini, apakah Anda keberatan saya mengajukan PR untuk mengimplementasikan MockAttribute seperti yang Anda tentukan?

@aivascu

@Kralizek ya silakan lakukan jika Anda telah menerapkannya. AFAIR ketika saya mencoba untuk mengimplementasikannya tidak semudah implementasi dari atas. Mari pindahkan diskusi ke #1269 .

Apakah halaman ini membantu?
0 / 5 - 0 peringkat