2012-05-15 5 views
36

мне нужно проверить следующий метод:Использование Moq для проверки вызовов сделаны в правильном порядке

CreateOutput(IWriter writer) 
{ 
    writer.Write(type); 
    writer.Write(id); 
    writer.Write(sender); 

    // many more Write()s... 
} 

Я создал Moq'd IWriter, и я хочу, чтобы убедиться, что 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 с сообщением "IWriter.Write() вызов с неудачной макет поведения Строго. Все вызовы на макет должны иметь соответствующую настройку. ".

Мне также сложно найти окончательную, современную документацию/примеры последовательностей Moq.

Я делаю что-то неправильно, или я не могу настроить последовательность, используя тот же метод? Если нет, есть ли альтернатива, которую я могу использовать (желательно с использованием Moq/NUnit)?

+0

возможный дубликат [Как проверить метод заказа вызова с Moq] (http://stackoverflow.com/questions/1765738/how- to-test-method-call-order-with-moq) – sloth

+0

[последняя версия Moq, v4.2] (https://github.com/Moq/moq) имеет «улучшенное тестирование последовательности подделок» в соответствии с ее [примечания к выпуску] (https://github.com/Moq/moq4/blob/master/ReleaseNotes.md). –

+1

Я использую v.4.2.x и могу подтвердить, что функциональность последовательности работает для меня. –

ответ

38

Есть ошибка, когда using MockSequence on same mock. Это определенно будет исправлено в более поздних выпусках библиотеки 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))); 
+1

Спасибо за информацию, я прибегал к использованию этого метода. Надеюсь, что позже будут выпущены Moq! Один из лучших инструментов тестирования есть ... –

+1

Одно слово предупреждения здесь, если метод 'Write()' никогда не вызывается, ни один из этих обратных вызовов не сможет что-либо утверждать. Обязательно добавьте catch, чтобы проверить, что метод вызывается хотя бы один раз. –

+0

Продолжается дискуссия о github относительно последовательности, для тех, кто хочет внести свой вклад/комментарий: https://github.com/moq/moq4/issues/75 – Ray

0

Я подозреваю, что ожидается, что это не то, что вы ожидаете.

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

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

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

+0

Я дважды проверял, и значения верны и как ожидалось. Рокинг моих собственных макетов - это то, что я стараюсь избегать, теперь я использую Moq, но, возможно, вам придется прибегать! И Verify() не может быть добавлен после настройки, только Verifiable() - что, к сожалению, не имеет значения. Спасибо за указатели. –

8

мне удалось получить поведение я хочу, но это требует загрузки библиотеки третьей стороны от 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 было бы неплохо увидеть.

+0

стыдно, что вы не можете пополудни. Прохладный не знал, что существовало: D. Но вы действительно стучите btw, как только вы попадете на эту территорию, и, возможно, почему moq немного не хватает в этой области - и да, проект кажется очень тихим. Посмотрел на его проблемы, и секвенция была там в течение 4 лет. Я также заметил, что последняя версия имеет окончательное название. –

3

Недавно я собрал две функции для Moq: VerifyInSequence() и VerifyNotInSequence(). Они работают даже с Loose Mocks.Однако, они доступны только в хранилище вилки MOQ:

https://github.com/grzesiek-galezowski/moq4

и ждать больше комментариев и тестирование, прежде чем принять решение о том, могут ли они быть включены в официальном MOq releaase. Однако ничто не мешает вам загружать исходный код в 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

Спасибо!

7

Я написал метод расширения, который будет утверждать на основании порядка вызова.

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 существует только одно объявление, все блокировки будут иметь ссылку на одну и ту же переменную. С expectedCallCount новый экземпляр создается каждой итерацией цикла (в отличие от простого использования 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); 
} 
2

Самым простым решением было бы с помощью Queue:

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