Вопрос по c# – Использование Moq для проверки звонков в правильном порядке
Мне нужно проверить следующий метод:
CreateOutput(IWriter writer)
{
writer.Write(type);
writer.Write(id);
writer.Write(sender);
// many more Write()s...
}
Я создал Moq 'dIWriter
и я хочу убедиться, чтоWrite()
методы вызываются в правильном порядке.
У меня есть следующий тестовый код:
var mockWriter = new Mock<IWriter>(MockBehavior.Strict);
var sequence = new MockSequence();
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedType));
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedId));
mockWriter.InSequence(sequence).Setup(x => x.Write(expectedSender));
Тем не менее, второй звонокWrite()
вCreateOutput()
(чтобы написатьid
значение) бросаетMockException
с сообщением & quot;IWriter.Write() invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup.& Quot ;.
Мне также трудно найти какую-либо конкретную, актуальную документацию / примеры последовательностей Moq.
Я делаю что-то не так, или я не могу настроить последовательность, используя тот же метод? Если нет, есть ли альтернатива, которую я могу использовать (предпочтительно используя Moq / NUnit)?
но для этого нужно загрузить стороннюю библиотеку сhttp://dpwhelan.com/blog/software-development/moq-sequences/
Затем последовательность может быть проверена с использованием следующего:
var mockWriter = new Mock<IWriter>(MockBehavior.Strict);
using (Sequence.Create())
{
mockWriter.Setup(x => x.Write(expectedType)).InSequence();
mockWriter.Setup(x => x.Write(expectedId)).InSequence();
mockWriter.Setup(x => x.Write(expectedSender)).InSequence();
}
Я добавил это как ответ частично, чтобы помочь документировать это решение, но я все еще интересуюсь, можно ли достичь чего-то подобного, используя только Moq 4.0.
Я не уверен, что Moq все еще находится в разработке, но исправляю проблему сMockSequence
или было бы полезно увидеть расширение moq-последовательностей в Moq.
Однако я, вероятно, просто напишу свою собственную реализацию IWriter, чтобы проверить в этом случае ... возможно, намного проще (и позже ее легче изменить).
Извините за отсутствие совета Moq напрямую. Мне это нравится, но я этого не делал.
Возможно, вам нужно добавить .Verify () в конце каждой настройки? (Это действительно предположение, хотя я и боюсь).
который будет утверждать на основе порядка вызова.
public static class MockExtensions
{
public static void ExpectsInOrder<T>(this Mock<T> mock, params Expression<Action<T>>[] expressions) where T : class
{
// All closures have the same instance of sharedCallCount
var sharedCallCount = 0;
for (var i = 0; i < expressions.Length; i++)
{
// Each closure has it's own instance of expectedCallCount
var expectedCallCount = i;
mock.Setup(expressions[i]).Callback(
() =>
{
Assert.AreEqual(expectedCallCount, sharedCallCount);
sharedCallCount++;
});
}
}
}
Он работает, используя преимущества того, как замыкания работают в отношении переменных области действия. Поскольку для sharedCallCount существует только одно объявление, все замыкания будут иметь ссылку на одну и ту же переменную. При использовании ожидаемого колла, новый экземпляр создается на каждой итерации цикла (в отличие от простого использования i в замыкании). Таким образом, у каждого замыкания есть только одна копия i, которая сравнивается с sharedCallCount при вызове выражений.
Вот небольшой модульный тест на расширение. Обратите внимание, что этот метод вызывается в разделе настроек, а не в разделе утверждений.
[TestFixture]
public class MockExtensionsTest
{
[TestCase]
{
// Setup
var mock = new Mock<IAmAnInterface>();
mock.ExpectsInOrder(
x => x.MyMethod("1"),
x => x.MyMethod("2"));
// Fake the object being called in order
mock.Object.MyMethod("1");
mock.Object.MyMethod("2");
}
[TestCase]
{
// Setup
var mock = new Mock<IAmAnInterface>();
mock.ExpectsInOrder(
x => x.MyMethod("1"),
x => x.MyMethod("2"));
// Fake the object being called out of order
Assert.Throws<AssertionException>(() => mock.Object.MyMethod("2"));
}
}
public interface IAmAnInterface
{
void MyMethod(string param);
}
используя MockSequence на том же макете, Это определенно будет исправлено в более поздних выпусках библиотеки Moq (вы также можете исправить это вручную, изменив реализацию Moq.MethodCall.Matches).
Если вы хотите использовать только Moq, то вы можете проверить порядок вызовов методов с помощью обратных вызовов:
int callOrder = 0;
writerMock.Setup(x => x.Write(expectedType)).Callback(() => Assert.That(callOrder++, Is.EqualTo(0)));
writerMock.Setup(x => x.Write(expectedId)).Callback(() => Assert.That(callOrder++, Is.EqualTo(1)));
writerMock.Setup(x => x.Write(expectedSender)).Callback(() => Assert.That(callOrder++, Is.EqualTo(2)));
VerifyInSequence () и VerifyNotInSequence (). Они работают даже с Loose Mocks. Однако они доступны только в ветке репозитория moq:
https://github.com/grzesiek-galezowski/moq4
и дождаться новых комментариев и тестирования, прежде чем принять решение о том, могут ли они быть включены в официальный moq releaseaase. Тем не менее, ничто не мешает вам загрузить исходный код в формате ZIP, встроить его в dll и попробовать. Используя эти функции, нужная вам проверка последовательности может быть записана так:
var mockWriter = new Mock<IWriter>() { CallSequence = new LooseSequence() }; //perform the necessary calls mockWriter.VerifyInSequence(x => x.Write(expectedType)); mockWriter.VerifyInSequence(x => x.Write(expectedId)); mockWriter.VerifyInSequence(x => x.Write(expectedSender));
(обратите внимание, что вы можете использовать две другие последовательности, в зависимости от ваших потребностей. Свободная последовательность разрешит любые вызовы между теми, которые вы хотите проверить. StrictSequence не допустит этого, а StrictAnytimeSequence похож на StrictSequence (нет вызовов методов между проверенными вызовами), но позволяет последовательность, которой должно предшествовать любое количество произвольных вызовов.
Если вы решили попробовать эту экспериментальную функцию, пожалуйста, прокомментируйте свои мысли: https://github.com/Moq/moq4/issues/21
Спасибо!