Weryfikacja określonego parametru za pomocą 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();
}

Zaczynam używać Moq i trochę się męczę. Próbuję zweryfikować, że messageServiceClient otrzymuje właściwy parametr, jakim jest XmlElement, ale nie mogę znaleźć sposobu, aby to działało. Działa tylko wtedy, gdy nie sprawdzam konkretnej wartości.

Jakieś pomysły?

Odpowiedź częściowa: Znalazłem sposób, aby sprawdzić, czy XML wysłany do proxy jest poprawny, ale nadal nie sądzę, że jest to właściwy sposób, aby to zrobić.

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

Przy okazji, jak mógłbym wyodrębnić wyrażenie z rozmowy weryfikacyjnej?

Author: Vitaliy Ulantikov, 2011-02-10

5 answers

Jeśli logika weryfikacji jest nietrywialna, będzie bałaganem napisać dużą metodę lambda (jak pokazuje Twój przykład). Możesz umieścić wszystkie instrukcje testowe w osobnej metodzie, ale nie lubię tego robić, ponieważ zakłóca to przepływ czytania kodu testowego.

Inną opcją jest użycie wywołania zwrotnego w wywołaniu Setup, aby zapisać wartość przekazaną do metody mocked, a następnie napisać standardowe metody Assert, aby ją zweryfikować. Na przykład:

// 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));
 168
Author: Rich Tebb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-26 09:34:59

Sprawdzałem połączenia w ten sam sposób-uważam, że to właściwy sposób.

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

Jeśli Twoje wyrażenie lambda stanie się nieporęczne, możesz utworzyć funkcję, która przyjmuje MyObject jako wejście i wyjście 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";
}

Należy również pamiętać o błędzie z Mockiem, gdzie komunikat o błędzie mówi, że metoda została wywołana wiele razy, gdy nie została wywołana w ogóle. Być może już to naprawili - ale jeśli widzisz ten Komunikat, możesz rozważyć sprawdzenie, czy metoda została właściwie nazwana.

EDIT: oto przykład wielokrotnego wywołania funkcji verify dla tych scenariuszy, w których chcesz sprawdzić, czy wywołujesz funkcję dla każdego obiektu na liście (na przykład).

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

To samo podejście do konfiguracji...

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

Więc za każdym razem, gdy GetStuff zostanie wywołany dla tego identyfikatora itemId, zwróci on rzeczy specyficzne dla tego elementu. Alternatywnie, możesz użyć funkcji, która przyjmuje itemId jako wejście i zwraca rzeczy.

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

Jedna inna metoda, którą widziałem na blog jakiś czas temu (może Phil Haack?) miał Setup wracający z jakiegoś obiektu dequeue - za każdym razem, gdy funkcja została wywołana, pobierał element z kolejki.

 58
Author: Mayo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-23 17:03:50

Prostszym sposobem byłoby:

ObjectA.Verify(
    a => a.Execute(
        It.Is<Params>(p => p.Id == 7)
    )
);
 12
Author: dmitry.sergeyev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-23 23:44:26

Wierzę, że problem polega na tym, że Moq sprawdzi równość. A ponieważ XmlElement nie nadpisuje równości, jej implementacja sprawdzi, czy nie ma równości odniesienia.

Nie można użyć obiektu niestandardowego, aby można było nadpisać równe?

 1
Author: Fernando,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-10 19:59:39

Miał JEDEN z nich, ale parametr akcji był interfejsem bez właściwości publicznych. Skończyło się na użyciu It.Is () z osobną metodą i w ramach tej metody musiałem zrobić jakieś wyśmiewanie interfejsu

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
}
 0
Author: ds4940,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-02 12:51:37