Используя Moq и посмотрел, Callback
но я не смог найти простой пример, чтобы понять, как его использовать.
У вас есть небольшой рабочий фрагмент, который четко объясняет, как и когда его использовать?
Ответы:
Трудно превзойти https://github.com/Moq/moq4/wiki/Quickstart
Если это недостаточно ясно, я бы назвал это ошибкой документа ...
РЕДАКТИРОВАТЬ: В ответ на ваше разъяснение ...
Для каждого Setup
выполняемого имитационного метода вы можете указать такие вещи, как:
.Callback
Механизм говорит : «Я не могу описать это прямо сейчас, но когда вызов в форме , как это происходит, перезвони мне , и я буду делать то , что должно быть сделано». В рамках той же самой цепочки вызовов вы можете управлять результатом для возврата (если есть) через .Returns
". В примерах QS примером является то, что они увеличивают возвращаемое значение каждый раз.
В общем, вам не понадобится такой механизм очень часто (в тестовых шаблонах xUnit есть термины для антипаттернов, подобных условной логике в тестах), и если есть более простой или встроенный способ установить, что вам нужно, он должен быть используется в предпочтении.
Об этом рассказывается в части 3 из 4 серии Moq Джастина Этериджа , и здесь есть еще один пример обратных вызовов.
Простой пример обратного вызова можно найти в статье Использование обратных вызовов с публикацией Moq .
Callback
имеет ничего общего с возвращаемым значением (если вы случайно не связали его с помощью кода). По сути, он только гарантирует, что обратный вызов вызывается до или после каждого вызова (в зависимости от того, связали ли вы его до или после Returns
соответственно), просто и просто.
Вот пример использования обратного вызова для тестирования объекта, отправленного в службу данных, которая обрабатывает вставку.
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");
It.Is<T>
in a Mock.Verify
вместо того, чтобы засорять тест темпами. Но +1, потому что я уверен, что есть много людей, которым лучше всего подойдет пример.
В Callback
Moq есть два типа . Один происходит до того, как вызов возвращается; другое происходит после возврата вызова.
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}");
});
В обоих обратных вызовах мы можем:
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
Кстати, пусть вас не смущает вводящее в заблуждение различие «до Returns
» и «после Returns
». Это просто техническое различие в том, будет ли ваш пользовательский код выполняться после Returns
оценки или раньше. В глазах вызывающего абонента оба будут выполняться до того, как будет возвращено значение. Действительно, если метод void
возвращается, вы даже не можете его вызвать, Returns
но он работает так же. Для получения дополнительной информации см. Https://stackoverflow.com/a/28727099/67824 .
Помимо других хороших ответов здесь, я использовал его для выполнения логики, прежде чем генерировать исключение. Например, мне нужно было сохранить все объекты, которые были переданы методу для последующей проверки, и этот метод (в некоторых тестовых случаях) должен был вызвать исключение. Вызов не .Throws(...)
на Mock.Setup(...)
переопределение в Callback()
действие и не вызывает ее. Однако, выбрасывая исключение в обратном вызове, вы по-прежнему можете выполнять все полезные действия, которые может предложить обратный вызов, и по-прежнему генерировать исключение.