Проверка определенного параметра с помощью Moq - PullRequest
138 голосов
/ 10 февраля 2011
public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully()
{
    var messageServiceClientMock = new Mock<IMessageServiceClient>();
    var queueableMessage = CreateSingleQueueableMessage();
    var message = queueableMessage[0];
    var xml = QueueableMessageAsXml(queueableMessage);
    messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(xml)).Verifiable();
    //messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(It.IsAny<XmlElement>())).Verifiable();

    var serviceProxyFactoryStub = new Mock<IMessageServiceClientFactory>();
    serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(essageServiceClientMock.Object);
    var loggerStub = new Mock<ILogger>();

    var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object);
    client.SubmitMessagesToQueue(new List<IMessageRequestDTO> {message});

    //messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(xml), Times.Once());
    messageServiceClientMock.Verify();
}

Я начинаю использовать Moq и немного борюсь. Я пытаюсь проверить, что messageServiceClient получает правильный параметр, который является XmlElement, но я не могу найти способ заставить его работать. Это работает только тогда, когда я не проверяю определенное значение.

Есть идеи?

Частичный ответ: Я нашел способ проверить, что xml, отправленный на прокси-сервер, верен, но я все еще не думаю, что это правильный способ.

public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully()
{
    var messageServiceClientMock = new Mock<IMessageServiceClient>();
    messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(It.IsAny<XmlElement>())).Verifiable();
    var serviceProxyFactoryStub = new Mock<IMessageServiceClientFactory>();
    serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(messageServiceClientMock.Object);
    var loggerStub = new Mock<ILogger>();

    var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object);
    var message = CreateMessage();
    client.SubmitMessagesToQueue(new List<IMessageRequestDTO> {message});

    messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(It.Is<XmlElement>(xmlElement => XMLDeserializer<QueueableMessage>.Deserialize(xmlElement).Messages.Contains(message))), Times.Once());
}

Кстати, как я могу извлечь выражение из вызова Verify?

Ответы [ 5 ]

204 голосов
/ 11 февраля 2011

Если логика проверки нетривиальна, будет сложно написать большой лямбда-метод (как показывает ваш пример).Вы можете поместить все тестовые операторы в отдельный метод, но мне не нравится это делать, потому что это нарушает поток чтения тестового кода.

Другой вариант - использовать обратный вызов в вызове Setup для сохранения значения, переданного в проверяемый метод, а затем написать стандартные Assert методы для его проверки.Например:

// Arrange
MyObject saveObject;
mock.Setup(c => c.Method(It.IsAny<int>(), It.IsAny<MyObject>()))
        .Callback<int, MyObject>((i, obj) => saveObject = obj)
        .Returns("xyzzy");

// Act
// ...

// Assert
// Verify Method was called once only
mock.Verify(c => c.Method(It.IsAny<int>(), It.IsAny<MyObject>()), Times.Once());
// Assert about saveObject
Assert.That(saveObject.TheProperty, Is.EqualTo(2));
81 голосов
/ 10 февраля 2011

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

mockSomething.Verify(ms => ms.Method(
    It.IsAny<int>(), 
    It.Is<MyObject>(mo => mo.Id == 5 && mo.description == "test")
  ), Times.Once());

Если ваше лямбда-выражение становится громоздким, вы можете создать функцию, которая принимает MyObject в качестве входных данных и выводит true / false ...

mockSomething.Verify(ms => ms.Method(
    It.IsAny<int>(), 
    It.Is<MyObject>(mo => MyObjectFunc(mo))
  ), Times.Once());

private bool MyObjectFunc(MyObject myObject)
{
  return myObject.Id == 5 && myObject.description == "test";
}

Также следует помнить об ошибке в Mock, когда в сообщении об ошибке говорится, что метод вызывался несколько раз, когда он вообще не вызывался. Возможно, они уже исправили это, но если вы видите это сообщение, вы можете проверить, действительно ли был вызван метод.

РЕДАКТИРОВАТЬ: Вот пример вызова проверки несколько раз для тех сценариев, где вы хотите проверить, что вы вызываете функцию для каждого объекта в списке (например).

foreach (var item in myList)
  mockRepository.Verify(mr => mr.Update(
    It.Is<MyObject>(i => i.Id == item.Id && i.LastUpdated == item.LastUpdated),
    Times.Once());

Тот же подход к настройке ...

foreach (var item in myList) {
  var stuff = ... // some result specific to the item
  this.mockRepository
    .Setup(mr => mr.GetStuff(item.itemId))
    .Returns(stuff);
}

Таким образом, каждый раз, когда GetStuff вызывается для этого itemId, он возвращает материал, специфичный для этого элемента. Кроме того, вы можете использовать функцию, которая принимает itemId в качестве входных данных и возвращает вещи.

this.mockRepository
    .Setup(mr => mr.GetStuff(It.IsAny<int>()))
    .Returns((int id) => SomeFunctionThatReturnsStuff(id));

Еще один метод, который я видел в блоге некоторое время назад (возможно, Фил Хаак?), Имел настройку, возвращающуюся из какого-либо объекта dequeue - каждый раз, когда вызывается функция, он вытягивает элемент из очереди.

15 голосов
/ 24 февраля 2017

Более простым способом было бы сделать:

ObjectA.Verify(
    a => a.Execute(
        It.Is<Params>(p => p.Id == 7)
    )
);
1 голос
/ 02 октября 2018

Был также один из них, но параметром действия был интерфейс без открытых свойств. Закончилось использованием It.Is () с отдельным методом, и в рамках этого метода пришлось выполнить некоторую проверку интерфейса

public interface IQuery
{
    IQuery SetSomeFields(string info);
}

void DoSomeQuerying(Action<IQuery> queryThing);

mockedObject.Setup(m => m.DoSomeQuerying(It.Is<Action<IQuery>>(q => MyCheckingMethod(q)));

private bool MyCheckingMethod(Action<IQuery> queryAction)
{
    var mockQuery = new Mock<IQuery>();
    mockQuery.Setup(m => m.SetSomeFields(It.Is<string>(s => s.MeetsSomeCondition())
    queryAction.Invoke(mockQuery.Object);
    mockQuery.Verify(m => m.SetSomeFields(It.Is<string>(s => s.MeetsSomeCondition(), Times.Once)
    return true
}
1 голос
/ 10 февраля 2011

Я считаю, что проблема в том, что Moq проверит на равенство. И, поскольку XmlElement не переопределяет Equals, его реализация будет проверять равенство ссылок.

Не можете ли вы использовать пользовательский объект, чтобы вы могли переопределить равно?

...