2011-02-10 16 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(); 
} 

Zaczynam używać Moq i nieco się z tym zmagam. Próbuję sprawdzić, czy messageServiceClient otrzymuje właściwy parametr, który jest XmlElement, ale nie mogę znaleźć żadnego sposobu, aby to działało. Działa tylko wtedy, gdy nie sprawdzam konkretnej wartości.Sprawdzanie określonego parametru za pomocą Moq

Wszelkie pomysły?

Częściowa odpowiedź: Znalazłem sposób na sprawdzenie, czy xml wysłany do proxy jest poprawny, ale nadal nie sądzę, że jest to właściwy sposób.

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

Nawiasem mówiąc, w jaki sposób mogę wyodrębnić wyrażenie z połączenia Verify?

Odpowiedz

149

Jeśli logika weryfikacji nie jest trywialna, pisanie dużej metody lambda (jak pokazuje Twój przykład) będzie kłopotliwe. Możesz umieścić wszystkie instrukcje testowe w osobnej metodzie, ale nie lubię tego robić, ponieważ zakłóca to przepływ odczytu kodu testowego.

Inną opcją jest użycie wywołania zwrotnego w wywołaniu Instalatora w celu zapisania wartości przekazanej do wyśmiewanej metody, a następnie napisanie standardowych metod Assert w celu sprawdzenia poprawności.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)); 
+5

Jedną dużą korzyścią dla tego podejścia jest to, że da ci konkretne niepowodzenia testowania, jak obiekt jest niepoprawny (jak testujesz każdy indywidualnie). –

+1

Myślałem, że jestem jedynym, który to zrobił, zadowolony, że to rozsądne podejście! –

+0

Myślę, że używanie It.Is (walidatora) jak na Mayo jest lepsze, ponieważ pozwala uniknąć nieco niezręcznego sposobu zapisywania wartości parametru jako części lambda – stevec

48

Sprawdzałem połączenia w ten sam sposób - uważam, że jest 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 wyrażenie lambda staje się niewydolny, można utworzyć funkcję, która pobiera MyObject jako wejścia i wyjścia prawda/fałsz ...

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

również zdawać sobie sprawę z błędu z udawaną gdzie komunikat o błędzie stwierdza, że ​​metoda została wywołana wiele razy, gdy w ogóle nie była wywoływana. Być może naprawili go już teraz - ale jeśli widzisz tę wiadomość, możesz rozważyć weryfikację, czy została ona rzeczywiście wywołana.

EDYCJA: Oto przykład wywoływania sprawdzania wiele razy 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()); 

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 GetStuff nazywa się w tym ItemID, powróci do tego elementu specyficzny materiał. Alternatywnie możesz użyć funkcji, która pobiera itemId jako dane wejściowe i zwraca rzeczy.

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

Jedna inna metoda Widziałam na blogu jakiś czas temu (? Phil Haack chyba) miał konfigurację powracającego z jakiegoś rozkolejkowania obiektu - za każdym razem funkcja została wywołana byłoby wyciągnąć element z kolejki.

+0

Dzięki, ma to sens dla mnie. To, czego wciąż nie rozumiem, to określenie szczegółów podczas konfiguracji lub weryfikacji. To jest dość zagmatwane. W tej chwili po prostu dopuszczam wszystko w Instalatorze i określanie wartości w Verify. –

+0

Jak myślisz, w jaki sposób mogę sprawdzić wiadomości w przypadku wielu połączeń? Klient odbierze wiadomości i może utworzyć wiele wiadomości w kolejce, które zakończą się wielokrotnymi połączeniami iw każdym z tych połączeń muszę sprawdzić różne wiadomości. Nadal walczę z testowaniem urządzeń w ogóle, nie jestem jeszcze zaznajomiony z tym. –

+0

Nie sądzę, że istnieje magiczna srebrna kula pod względem tego, jak należy to zrobić. To wymaga praktyki i zaczynasz się poprawiać. Dla mnie określam tylko parametry, kiedy mam coś do porównania i kiedy nie testuję tego parametru w innym teście. Jeśli chodzi o wiele połączeń, istnieje kilka podejść. Aby skonfigurować i zweryfikować funkcję, która jest wywoływana wiele razy, zwykle wywołuję konfigurację lub weryfikację (Times.Once()) dla każdego wywołania, jakiego oczekuję - często z pętlą for. Możesz użyć określonych parametrów, aby odizolować każde połączenie. – Mayo

1

Wierzę, że problem polega na tym, że Moq sprawdzi równość. A ponieważ XmlElement nie zastępuje Equals, jego implementacja sprawdzi równość referencyjną.

Nie można użyć niestandardowego obiektu, aby można było przesłonić równe wartości?

+0

Tak, w końcu to zrobiłem. Zdałem sobie sprawę, że problemem było sprawdzenie Xml. W drugiej części pytania dodałem możliwą odpowiedź deserializing xml do obiektu –

8

Prostszym sposobem byłoby zrobić:

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