Проверка конкретного параметра с помощью Moq


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 111

5 ответов:

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

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

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

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";
}

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

изменить: Вот пример вызова проверки несколько раз для тех сценариев, где вы хотите проверить, что вы вызываете функцию для каждого объекта в списке (например).

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

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

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

ObjectA.Verify(
    a => a.Execute(
        It.Is<Params>(p => p.Id == 7)
    )
);

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

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

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