Bisakah Anda membantu saya memahami Moq Callback?

96

Menggunakan Moq dan melihat Callbacktetapi saya belum dapat menemukan contoh sederhana untuk memahami cara menggunakannya.

Apakah Anda memiliki potongan kecil yang berfungsi yang dengan jelas menjelaskan bagaimana dan kapan menggunakannya?

pengguna9969
sumber

Jawaban:

83

Sulit dikalahkan https://github.com/Moq/moq4/wiki/Quickstart

Jika itu tidak cukup jelas, saya akan menyebutnya bug dokumen ...

EDIT: Sebagai tanggapan atas klarifikasi Anda ...

Untuk setiap metode tiruan yang SetupAnda lakukan, Anda dapat menunjukkan hal-hal seperti:

  • kendala input
  • nilai untuk / cara di mana nilai kembali (jika ada) akan diturunkan

The .CallbackMekanisme mengatakan "Saya tidak bisa menjelaskan sekarang, tapi ketika panggilan berbentuk seperti ini terjadi, memanggil saya kembali dan saya akan melakukan apa yang perlu dilakukan". Sebagai bagian dari rantai panggilan fasih yang sama, Anda dapat mengontrol hasil untuk dikembalikan (jika ada) melalui .Returns". Dalam contoh QS, contohnya adalah mereka membuat nilai yang dikembalikan meningkat setiap waktu.

Secara umum, Anda tidak akan membutuhkan mekanisme seperti ini terlalu sering (Pola Tes xUnit memiliki istilah untuk antipatterns dari Tes Logika Bersyarat sejenisnya), dan jika ada cara yang lebih sederhana atau built-in untuk menetapkan apa yang Anda butuhkan, itu harus digunakan dalam preferensi.

Bagian 3 dari 4 dalam seri Moq Justin Etheredge mencakupnya, dan ada contoh callback lain di sini

Contoh sederhana dari panggilan balik dapat ditemukan di Menggunakan Panggilan Balik dengan posting Moq .

Ruben Bartelink
sumber
3
Hai Ruben, saya belajar Moq dan jika Anda suka, saya memberikan banyak contoh untuk memahami cara melakukan sesuatu dengan menggunakannya. Masalah saya adalah saya tidak mengerti kapan harus menggunakannya. Setelah saya mengerti masalah itu terpecahkan, saya akan menulis kode saya sendiri. Jika Anda menjelaskannya dengan kata-kata Anda sendiri kapan Anda akan menggunakan panggilan balik? terima kasih menghargai waktu Anda
pengguna9969
15
Sulit dikalahkan [link]? Tidak semuanya. Tautan itu menunjukkan kepada Anda bagaimana melakukan lusinan hal yang berbeda, tetapi tidak memberi tahu Anda mengapa Anda perlu melakukan salah satunya. Yang merupakan masalah umum dalam dokumentasi ejekan, saya temukan. Saya dapat mengandalkan dengan nol jari jumlah penjelasan yang baik dan jelas tentang ejekan TDD + yang saya temukan. Sebagian besar mengasumsikan tingkat pengetahuan yang, jika saya memilikinya, saya tidak perlu membaca artikel itu.
Ryan Lundy
@Yralessa: Saya mengerti maksud Anda. Saya pribadi memiliki cukup banyak pengetahuan buku yang masuk sehingga menemukan hal-hal quickstart benar-benar sempurna. Sayangnya saya tidak mengetahui contoh yang lebih baik yang saya tautkan di akhir posting. Jika Anda menemukannya, posting di sini dan saya akan dengan senang hati mengeditnya di (atau silakan ke DIY)
Ruben Bartelink
"Saya akan melakukan apa yang perlu dilakukan dan memberi tahu Anda hasil untuk dikembalikan (jika ada)" Saya pikir ini menyesatkan, AFAIU Callbacktidak ada hubungannya dengan nilai pengembalian (kecuali jika Anda menghubungkannya melalui kode). Pada dasarnya ini hanya memastikan callback dipanggil sebelum atau setelah setiap pemanggilan (tergantung apakah Anda menghubungkannya sebelum atau sesudah masing- Returnsmasing), polos dan sederhana.
Ohad Schneider
1
@OhadSchneider Mengikuti tautan saya ... Anda benar! Bertanya-tanya (tetapi tidak terlalu tertarik karena belum menggunakan Moq untuk waktu yang lama) jika antarmuka Fluent telah berubah (sepertinya tidak mungkin, yaitu saya membuat asumsi yang salah dan tidak membaca hal yang saya tautkan seperti biasanya saya mengerjakannya dari pelengkapan otomatis sendiri). Semoga perbaikan ini dapat menjawab maksud Anda, beri tahu saya jika tidak
Ruben Bartelink
60

Berikut adalah contoh penggunaan callback untuk menguji entitas yang dikirim ke Layanan Data yang menangani penyisipan.

var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;

mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1) 
           .Callback((DataEntity de) => insertedEntity = de);

Sintaks metode generik alternatif:

mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1) 
           .Callback<DataEntity>(de => insertedEntity = de);

Kemudian Anda dapat menguji sesuatu seperti

Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");
Jeff Hall
sumber
4
Bisa dibilang untuk kasus tertentu (tergantung pada apakah Anda mencoba untuk mengekspresikan pengujian terhadap status atau perilaku), mungkin dalam beberapa kasus lebih bersih untuk menggunakan It.Is<T>in a Mock.Verifydaripada mengotori pengujian dengan temps. Tetapi +1 karena saya yakin ada banyak orang yang akan bekerja paling baik dari sebuah contoh.
Ruben Bartelink
10

Ada dua jenis Callbackdalam Moq. Satu terjadi sebelum panggilan kembali; yang lainnya terjadi setelah panggilan kembali.

var message = "";
mock.Setup(foo => foo.Execute(arg1: "ping", arg2: "pong"))
    .Callback((x, y) =>
    {
        message = "Rally on!";
        Console.WriteLine($"args before returns {x} {y}");
    })
    .Returns(message) // Rally on!
    .Callback((x, y) =>
    {
        message = "Rally over!";
        Console.WriteLine("arg after returns {x} {y}");
    });

Di kedua panggilan balik, kita dapat:

  1. periksa argumen metode
  2. menangkap argumen metode
  3. mengubah keadaan kontekstual
Shaun Luttin
sumber
2
Sebenarnya, keduanya terjadi sebelum panggilan kembali (sejauh menyangkut pemanggil). Lihat stackoverflow.com/a/28727099/67824 .
Ohad Schneider
6

Callbackhanyalah sarana untuk mengeksekusi kode kustom yang Anda inginkan saat panggilan dilakukan ke salah satu metode tiruan. Berikut contoh sederhananya:

public interface IFoo
{
    int Bar(bool b);
}

var mock = new Mock<IFoo>();

mock.Setup(mc => mc.Bar(It.IsAny<bool>()))
    .Callback<bool>(b => Console.WriteLine("Bar called with: " + b))
    .Returns(42);

var ret = mock.Object.Bar(true);
Console.WriteLine("Result: " + ret);

// output:
// Bar called with: True
// Result: 42

Saya baru-baru ini menemukan kasus penggunaan yang menarik untuk itu. Misalkan Anda mengharapkan beberapa panggilan ke tiruan Anda, tetapi itu terjadi secara bersamaan. Jadi, Anda tidak memiliki cara untuk mengetahui urutan panggilan mereka, tetapi Anda ingin mengetahui panggilan yang Anda harapkan terjadi (terlepas dari urutannya). Anda bisa melakukan sesuatu seperti ini:

var cq = new ConcurrentQueue<bool>();
mock.Setup(f => f.Bar(It.IsAny<bool>())).Callback<bool>(cq.Enqueue);
Parallel.Invoke(() => mock.Object.Bar(true), () => mock.Object.Bar(false));
Console.WriteLine("Invocations: " + String.Join(", ", cq));

// output:
// Invocations: True, False

BTW tidak bingung dengan perbedaan "sebelum Returns" dan "setelah Returns" yang menyesatkan . Ini hanyalah perbedaan teknis tentang apakah kode kustom Anda akan berjalan setelah Returnsdievaluasi atau sebelumnya. Di mata pemanggil, keduanya akan berjalan sebelum nilainya dikembalikan. Memang, jika metodenya void-kembali, Anda bahkan tidak dapat memanggil Returnsnamun berfungsi sama. Untuk informasi lebih lanjut, lihat https://stackoverflow.com/a/28727099/67824 .

Ohad Schneider
sumber
1

Di atas jawaban bagus lainnya di sini, saya telah menggunakannya untuk melakukan logika sebelum membuat pengecualian. Misalnya, saya perlu menyimpan semua objek yang diteruskan ke suatu metode untuk verifikasi nanti, dan metode itu (dalam beberapa kasus uji) perlu membuat pengecualian. Memanggil .Throws(...)pada Mock.Setup(...)menimpa para Callback()tindakan dan tidak pernah menyebutnya. Namun, dengan memberikan pengecualian dalam Callback, Anda masih dapat melakukan semua hal baik yang ditawarkan oleh callback, dan tetap memberikan pengecualian.

Frank Bryce
sumber