2011-02-10 2 views
100
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, но я не могу найти способ заставить его работать. Он работает только тогда, когда я не проверю определенное значение.Проверка определенного параметра с помощью Moq

Любые идеи?

Частичный ответ: Я нашел способ проверить, что 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?

ответ

149

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

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

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

+1

Я думал, что я был единственным, кто сделал это, рад видеть, что это разумный подход! –

+0

Я думаю, что использовать It.Is (валидатор) в соответствии с Mayo лучше, так как он избегает слегка неудобного способа сохранения значения параметра как части лямбда – stevec

48

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

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

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

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

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

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

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

+0

Спасибо, это имеет смысл для меня. То, что я до сих пор не могу понять, - это когда указывать детали в Setup или Verify. Это довольно запутанно. На данный момент я просто разрешаю что-либо в программе установки и указывая значения в Verify. –

+0

Как вы думаете, я могу проверять сообщения при наличии нескольких вызовов? Клиент принимает сообщения и может создавать несколько queueableMessages, которые заканчиваются несколькими вызовами и в каждом из этих вызовов я должен проверять разные сообщения. Я все еще борюсь с модульным тестированием в целом, я еще не знаком с ним. –

+0

Я не думаю, что есть волшебная серебряная пуля с точки зрения того, как вы должны это делать. Это требует практики, и вы начинаете становиться лучше. Для меня я задаю только параметры, когда мне есть с чем сравнивать, и когда я еще не тестировал этот параметр в другом тесте. Что касается нескольких вызовов, существует несколько подходов. Для настройки и проверки функции, вызываемой несколько раз, я обычно вызываю установку или проверку (Times.Once()) для каждого ожидаемого вызова - часто с циклом for. Вы можете использовать определенные параметры для изоляции каждого вызова. – Mayo

1

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

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

+0

Да, я закончил это. Я понял, что проблема заключается в проверке Xml. Во второй части вопроса я добавил возможный ответ, десериализирующий xml для объекта. –

8

Более простой способ будет сделать:

ObjectA.Verify(
    a => a.Execute(
     It.Is<Params>(p => p.Id == 7) 
    ) 
); 
Смежные вопросы