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

public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully() { var messageServiceClientMock = new Mock(); 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())).Verifiable(); var serviceProxyFactoryStub = new Mock(); serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(essageServiceClientMock.Object); var loggerStub = new Mock(); var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object); client.SubmitMessagesToQueue(new List {message}); //messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(xml), Times.Once()); messageServiceClientMock.Verify(); } 

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

Есть идеи?

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

 public void SubmitMessagesToQueue_OneMessage_SubmitSuccessfully() { var messageServiceClientMock = new Mock(); messageServiceClientMock.Setup(proxy => proxy.SubmitMessage(It.IsAny())).Verifiable(); var serviceProxyFactoryStub = new Mock(); serviceProxyFactoryStub.Setup(proxyFactory => proxyFactory.CreateProxy()).Returns(messageServiceClientMock.Object); var loggerStub = new Mock(); var client = new MessageClient(serviceProxyFactoryStub.Object, loggerStub.Object); var message = CreateMessage(); client.SubmitMessagesToQueue(new List {message}); messageServiceClientMock.Verify(proxy => proxy.SubmitMessage(It.Is(xmlElement => XMLDeserializer.Deserialize(xmlElement).Messages.Contains(message))), Times.Once()); } 

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

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

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

 // Arrange MyObject saveObject; mock.Setup(c => c.Method(It.IsAny(), It.IsAny())) .Callback((i, obj) => saveObject = obj) .Returns("xyzzy"); // Act // ... // Assert // Verify Method was called once only mock.Verify(c => c.Method(It.IsAny(), It.IsAny()), Times.Once()); // Assert about saveObject Assert.That(saveObject.TheProperty, Is.EqualTo(2)); 

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

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

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

 mockSomething.Verify(ms => ms.Method( It.IsAny(), It.Is(mo => MyObjectFunc(mo)) ), Times.Once()); private bool MyObjectFunc(MyObject myObject) { return myObject.Id == 5 && myObject.description == "test"; } 

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

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

 foreach (var item in myList) mockRepository.Verify(mr => mr.Update( It.Is(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())) .Returns((int id) => SomeFunctionThatReturnsStuff(id)); 

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

Более простой способ:

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

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

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

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

 public interface IQuery { IQuery SetSomeFields(string info); } void DoSomeQuerying(Action queryThing); mockedObject.Setup(m => m.DoSomeQuerying(It.Is>(q => MyCheckingMethod(q))); private bool MyCheckingMethod(Action queryAction) { var mockQuery = new Mock(); mockQuery.Setup(m => m.SetSomeFields(It.Is(s => s.MeetsSomeCondition()) queryAction.Invoke(mockQuery.Object); mockQuery.Verify(m => m.SetSomeFields(It.Is(s => s.MeetsSomeCondition(), Times.Once) return true }