Можете ли вы помочь мне понять Moq Callback?

Используя Moq и посмотрев на Callback, но я не смог найти простой пример, чтобы понять, как его использовать.

Есть ли у вас небольшой рабочий фрагмент, который четко объясняет, как и когда его использовать?

Ответ 1

Тяжело бить https://github.com/Moq/moq4/wiki/Quickstart

Если это не ясно, я бы назвал это ошибкой doc...

EDIT: В ответ на ваше разъяснение...

Для каждого из насмешанного метода Setup, который вы выполняете, вы можете указать такие вещи, как:

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

Механизм .Callback говорит: "Я не могу описать его прямо сейчас, но когда возникает такой звонок, позвоните мне, и я сделаю то, что нужно сделать". В рамках одной и той же безвозвратной цепочки вызовов вы можете контролировать результат для возврата (если есть) через .Returns ". В примерах QS примером является то, что они возвращают возвращаемое значение каждый раз.

В общем, вам не понадобится такой механизм, как это очень часто (xUnit Test Patterns имеет термины для antipatterns Ilk Conditional Logic In Tests), и если есть более простой или встроенный способ установления того, что вам нужно, его следует использовать в предпочтении.

Часть 3 из 4 в серии Justin Etheredge Moq охватывает ее, и здесь есть другой пример обратных вызовов

Ответ 2

Здесь приведен пример использования обратного вызова для проверки объекта, отправленного в службу данных, которая обрабатывает вставку.

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

Ответ 3

В moq есть два типа 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}");
    });

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

  • проверить аргументы метода
  • метод захвата аргументов
  • изменить контекстное состояние

Ответ 4

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

Ответ 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, и все же он работает одинаково. Для получения дополнительной информации см. fooobar.com/info/84640/....