Чи можете ви допомогти мені зрозуміти зворотній зв'язок Moq?


95

Використовуючи Moq і переглянув, Callbackале мені не вдалося знайти простий приклад, щоб зрозуміти, як ним користуватися.

У вас є невеликий робочий фрагмент, який чітко пояснює, як і коли його використовувати?

Відповіді:


83

Важко перемогти https://github.com/Moq/moq4/wiki/Quickstart

Якщо це недостатньо зрозуміло, я б назвав цю помилку в документі ...

EDIT: У відповідь на ваше пояснення ...

Для кожного методу глузування, який Setupви виконуєте, ви можете вказати такі речі, як:

  • обмеження на входи
  • значення для / способу, яким має бути отримано повернене значення (якщо воно є)

.CallbackМеханізм каже : «Я не можу описати це прямо зараз, але коли виклик в формі , як це відбувається, передзвони мені , і я буду робити те , що повинно бути зроблено». Як частина одного і того ж ланцюга вільних викликів, ви можете керувати результатом, щоб повернутись (якщо така є) через .Returns". У прикладах QS приклад полягає в тому, що вони змушують повертати значення щоразу збільшуватися.

Взагалі, вам не знадобиться такий механізм, як це часто (тестові шаблони xUnit мають терміни для антипакетів нерегулярних умовних логічних тестів), і якщо є якийсь простіший або вбудований спосіб встановити те, що вам потрібно, це має бути використовується в перевазі.

Частина 3 із 4 у серії Moq Джастіна Етередже висвітлює його, і тут є ще один приклад зворотних викликів

Простий приклад зворотного дзвінка можна знайти в розділі Використання зворотних дзвінків з повідомленням Moq .


3
Привіт Рубене, я вчу Moq, і якщо вам подобається, я складаю безліч прикладів, щоб зрозуміти, як робити речі, використовуючи його. Моя проблема полягає в тому, що я не розумію, коли її використовувати. Після того, як я зрозумію, що проблема вирішена, я напишу власний код. Якби ви пояснили це своїм словом, коли ви будете використовувати зворотний дзвінок? спасибі,
цінуй

15
Важко перемогти [посилання]? Зовсім ні. Це посилання показує вам, як можна робити десятки різних речей, але не говорить про те, чому вам потрібно робити будь-яку з них. Яка проблема є спірною документацією. Я можу порахувати на нуль пальців кількість хороших, чітких пояснень TDD + насмішок, які я знайшов. Більшість припускає такий рівень знань, який, якби я його мав, мені не потрібно було б читати статтю.
Райан Лунді

@Kyralessa: Я сприймаю вашу думку. Я особисто мав досить багато книжкових знань, тому знайшов, що швидкий старт був абсолютно ідеальним. На жаль, я не знаю кращого прикладу, що ті, з якими я посилався в кінці посту. Якщо ви знайдете його, опублікуйте його тут, і я буду радий відредагувати його в (або не соромтеся робити сам)
Ruben Bartelink

"Я зроблю те, що потрібно зробити, і скажу вам результат, який потрібно повернути (якщо такий є)" Я думаю, що це вводить в оману, AFAIU Callbackне має нічого спільного з значенням повернення (якщо ви не зв'язали його через код). По суті, це лише гарантує виклик зворотного дзвінка перед або після кожного виклику (залежно від того, ви прикували його до або після Returnsвідповідно), простий і простий.
Охад Шнайдер

1
@OhadSchneider Перейшовши за моїм посиланням ... ти прав! Дивовижно (але не дуже цікаво, оскільки я не використовував Moq протягом довгого часу), якщо інтерфейс Fluent змінився (не здається, ймовірно, тобто я зробив помилкове припущення, і не читав те, з чим я пов’язаний, як зазвичай працював у ньому від автозавершення себе). Сподіваюсь, що виправлення вирішує вашу точку, дайте мені знати, якщо це не так
Рубен Бартелінк

59

Ось приклад використання зворотного дзвінка для тестування сутності, надісланої до служби даних, яка обробляє вставку.

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

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

Альтернативний синтаксис загального методу:

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

Тоді ви можете випробувати щось на кшталт

Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");

4
Можливо для цього конкретного випадку ( в залежності від того , що ви намагаєтеся висловити тести проти стану або поведінки), то в деяких випадках може бути чистіше , щоб використовувати It.Is<T>в Mock.Verifyзамість засмічення тесту з темпами. Але +1, бо я думаю, що є багато людей, які найкраще працюватимуть із прикладу.
Рубен Бартелінк

10

У CallbackМок є два типи . Це трапляється до повернення дзвінка; інше відбувається після повернення дзвінка.

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}");
    });

В обох зворотних викликах ми можемо:

  1. перевірити аргументи методу
  2. аргументи методу захоплення
  3. змінити контекстний стан

2
Насправді, і те, і інше відбувається до повернення дзвінка (що стосується абонента). Дивіться stackoverflow.com/a/28727099/67824 .
Охад Шнайдер

5

Callbackце просто засіб для виконання будь-якого користувальницького коду, який ви хочете, коли дзвінок виконується одним із методів макету. Ось простий приклад:

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

Нещодавно я зіткнувся з цікавим випадком використання для цього. Припустимо, ви очікуєте деяких дзвінків на ваш глум, але вони відбуваються одночасно. Таким чином, у вас немає можливості дізнатися порядок, в якому вони будуть дзвонити, але ви хочете дізнатися, що дзвінки, які ви очікували, відбулися (незалежно від порядку). Ви можете зробити щось подібне:

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 не плутають оманливу різницю "до Returns" і "після Returns". Це лише технічна відмінність того, чи буде працювати ваш власний код після того, Returnsяк було оцінено чи раніше. В очах абонента обидва будуть працювати до повернення значення. Дійсно, якщо метод - voidповернення, ви навіть не можете зателефонувати, Returnsале він працює так само. Для отримання додаткової інформації див https://stackoverflow.com/a/28727099/67824 .


1

Крім інших хороших відповідей тут, я використовував її для виконання логіки перед тим, як кидати виняток. Наприклад, мені потрібно було зберігати всі об'єкти, передані методу для подальшої перевірки, і цей метод (у деяких тестових випадках) необхідний для викиду винятку. Виклик не .Throws(...)на Mock.Setup(...)перевизначення в Callback()дію і не викликає її. Однак, викинувши виняток у межах зворотного виклику, ви все одно можете зробити все хороше, що може запропонувати зворотний дзвінок, і все ж кинути виняток.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.