Bagaimana cara saya Moq metode yang memiliki argumen opsional dalam tanda tangannya tanpa secara eksplisit menentukannya atau menggunakan kelebihan beban?

119

Diberikan antarmuka berikut:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

Mencoba mengejeknya menggunakan Moq:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);

memberikan kesalahan berikut pada waktu kompilasi:

Pohon ekspresi tidak boleh berisi panggilan atau pemanggilan yang menggunakan argumen opsional

Saya telah menemukan masalah di atas diangkat sebagai peningkatan dalam daftar masalah Moq dan tampaknya ditugaskan ke rilis 4.5 (kapan pun itu).

Pertanyaan saya adalah: apa yang harus saya lakukan mengingat hal di atas tidak akan diperbaiki dalam waktu dekat? Apakah pilihan saya hanya untuk secara eksplisit mengatur nilai default dari parameter opsional setiap kali saya mengejeknya (yang mengalahkan titik menentukan satu di tempat pertama) atau untuk membuat kelebihan tanpa bool (seperti apa yang akan saya lakukan? sebelum C # 4)?

Atau adakah yang menemukan cara yang lebih cerdas untuk mengatasi masalah ini?

Appulus
sumber
5
Apakah masuk akal untuk hanya menentukan It.IsAny <bool> () untuk parameter kedua?
Paul d'Aoust
Satu setengah tahun kemudian ini masih benar ..
Mukus
@Mukus, jangan ragu untuk drop PR ya bro.
IamDOM

Jawaban:

91

Saya yakin satu-satunya pilihan Anda saat ini adalah menyertakan boolparameter secara eksplisit dalam penyiapan untuk Foo.

Saya tidak berpikir itu mengalahkan tujuan menentukan nilai default. Nilai default adalah kemudahan untuk memanggil kode, tetapi menurut saya Anda harus eksplisit dalam pengujian Anda. Katakanlah Anda tidak perlu menentukan boolparameter. Apa yang terjadi jika, di masa mendatang, seseorang mengubah nilai default bmenjadi true? Hal ini akan menyebabkan pengujian gagal (dan memang seharusnya demikian), tetapi pengujian tersebut akan lebih sulit untuk diperbaiki karena asumsi tersembunyi btersebut false. Menentukan boolparameter secara eksplisit memiliki manfaat lain: meningkatkan keterbacaan pengujian Anda. Seseorang yang melewatinya akan segera mengetahui bahwa ada satu Foofungsi yang menerima dua parameter. Itu 2 sen saya, setidaknya :)

Sedangkan untuk menentukannya setiap kali Anda memalsukannya, jangan menduplikasi kode: buat dan / atau inisialisasi tiruan dalam sebuah fungsi, sehingga Anda hanya memiliki satu titik perubahan. Jika Anda benar-benar ingin, Anda dapat mengatasi kekurangan Moq yang terlihat di sini dengan menduplikasi Fooparameter ke dalam fungsi inisialisasi ini:

public void InitFooFuncOnFooMock(Mock<IFoo> fooMock, string a, bool b = false)
{
    if(!b)
    {
        fooMock.Setup(mock => mock.Foo(a, b)).Returns(false);
    }
    else
    {
        ...
    }
}
Chris Mantle
sumber
1
Jawaban yang bagus; Saya sudah melanjutkan dan menentukannya secara eksplisit di ejekan saya tetapi jawaban Anda menegaskan dengan cara yang sangat jelas dan logis mengapa saya harus melakukannya dengan cara ini. Terima kasih, @Chris.
Appulus
9
mengubah parameter default "harus" menghentikan pengujian. Tidak adanya pengujian yang gagal saat default diubah dapat menjadi tanda pengujian yang buruk. Kode dapat menggunakan default tetapi tes tidak?
Pop Catalin
Sudah lama, tetapi saya sudah mencoba pendekatan ini dengan Moq ketika mencoba mengejek antarmuka (IDConnection di Dapper) dan saya masih mendapatkan kesalahan yang sama. Ada ide mengapa? Baris contoh: mockDB.Setup (x => x.Query <MyObject> (It.IsAny <string> (), It.IsAny <DynamicParameters> (), It.IsAny <IDbTransaction> (), false, 600)). Returns (List baru <MyObject> ()); Dua nilai terakhir adalah parameter opsional pada metode yang saya siapkan.
Raelshark
4
Arrgggh! if (!x) {} else {}Anti-pola yang mengerikan :)
nicodemus13
1
@ nicodemus13 Ya, tapi saya mencoba untuk menjaga contoh kode sedekat mungkin dengan contoh OP dalam pertanyaan. Saya tidak akan selalu menganjurkannya :)
Chris Mantle
8

Baru saja mengalami masalah ini hari ini, Moq tidak mendukung kasus penggunaan ini. Jadi, tampaknya meng-override metode ini sudah cukup untuk kasus ini.

public interface IFoo
{
    bool Foo(string a);

    bool Foo(string a, bool b);
}

Sekarang kedua metode tersebut tersedia dan contoh ini akan berfungsi:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);
Gil Grossman
sumber
2

Dengan menggunakan Moq versi 4.10.1, saya telah dapat melakukan hal berikut

Dengan Antarmuka:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

Dan Mock

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>(), It.IsAny<bool>())).Returns(false);

Menyelesaikan panggilan ke Foo dengan parameter pertama oke

CF5
sumber