Вопрос по c# – Использование Moq для проверки звонков в правильном порядке

47

Мне нужно проверить следующий метод:

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)?

latest release of Moq, v4.2 имеет «улучшенное тестирование последовательностей ложных вызовов»; в соответствии с егоrelease notes. g t
возможный дубликатHow to test method call order with Moq sloth
Я использую v.4.2.x и могу подтвердить, что функциональность последовательности работает для меня. Martin Capodici

Ваш Ответ

6   ответов
10

но для этого нужно загрузить стороннюю библиотеку с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.

Error: User Rate Limit Exceeded
0

Однако я, вероятно, просто напишу свою собственную реализацию IWriter, чтобы проверить в этом случае ... возможно, намного проще (и позже ее легче изменить).

Извините за отсутствие совета Moq напрямую. Мне это нравится, но я этого не делал.

Возможно, вам нужно добавить .Verify () в конце каждой настройки? (Это действительно предположение, хотя я и боюсь).

Error: User Rate Limit Exceeded g t
2

Очередь:

var expectedParameters = new Queue<string>(new[]{expectedType,expectedId,expectedSender});
mockWriter.Setup(x => x.Write(expectedType))
          .Callback((string s) => Assert.AreEqual(expectedParameters.Dequeue(), s));
8

который будет утверждать на основе порядка вызова.

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

используя 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)));
Error: User Rate Limit Exceeded g t
Error: User Rate Limit ExceededWrite()Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededgithub.com/moq/moq4/issues/75
4

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

Спасибо!

Похожие вопросы