Argument Matching w Moq (np. It.Is, It.IsAny, Verify)

Argument Matching w Moq (np. It.Is, It.IsAny, Verify)
1 maja 2024 Brak komentarzy Testowanie Tajko

Argument Matching to kluczowy aspekt tworzenia elastycznych i wyrafinowanych testów jednostkowych przy użyciu biblioteki Moq w C#. Pozwala on na specyfikację, które wywołania metod powinny być dopasowane przez mocka na podstawie argumentów, które są do nich przekazywane. Dzięki zastosowaniu różnorodnych matcherów argumentów, testujący może precyzyjnie kontrolować zachowanie mocka, co jest szczególnie przydatne w scenariuszach, gdzie metoda jest wywoływana wielokrotnie z różnymi parametrami lub kiedy chcemy potwierdzić zachowanie metody w szczególnie skomplikowanych przypadkach biznesowych.

Klasy i Metody It

It.Equals

W Moq, It.Equals nie jest używany jako metoda dopasowująca. Moq korzysta z It.Is<T>() do dopasowania konkretnych wartości. Zamiast It.Equals, użyj It.Is<T>() z równością jako warunkiem.

It.Is

It.Is<T>() pozwala zdefiniować warunek dla argumentu, który musi być spełniony, aby mock został dopasowany.

// Ustawienie zachowania mocka dla metody, która przyjmuje argument int spełniający określony warunek.
mock.Setup(m => m.Execute(It.Is<int>(x => x > 0))).Returns(true);
It.IsAny

Metoda It.IsAny<T>() jest używana do określenia, że dowolny argument danego typu T jest akceptowalny.

// Ustawienie zachowania mocka dla metody, która przyjmuje dowolny argument typu int.
mock.Setup(m => m.Execute(It.IsAny<int>())).Returns(true);
It.IsAnyType

It.IsAnyType nie jest standardową funkcją w Moq. Moq umożliwia dopasowanie dowolnych typów za pomocą It.IsAny<T>().

It.IsIn

It.IsIn pozwala sprawdzić, czy argument jest jednym z elementów określonej kolekcji.

// Ustawienie zachowania mocka dla metody, która przyjmuje argument będący jednym z określonych wartości.
mock.Setup(m => m.Execute(It.IsIn<int>(new int[] { 1, 2, 3 }))).Returns(true);
It.IsInRange

It.IsInRange<T>() dopasowuje argumenty, które mieszczą się w określonym zakresie.

// Ustawienie zachowania mocka dla metody, która przyjmuje argument int w zakresie od 1 do 10.
mock.Setup(m => m.Execute(It.IsInRange<int>(1, 10, Range.Inclusive))).Returns(true);
It.IsNotIn

It.IsNotIn pozwala sprawdzić, czy argument nie jest częścią określonej kolekcji.

// Ustawienie zachowania mocka dla metody, która przyjmuje argument nie będący w określonej kolekcji.
mock.Setup(m => m.Execute(It.IsNotIn<int>(new int[] { 1, 2, 3 }))).Returns(true);
It.IsNotnull

It.IsNotNull<T>() dopasowuje argumenty, które nie są null.

// Ustawienie zachowania mocka dla metody, która przyjmuje argument typu, który nie jest null.
mock.Setup(m => m.Execute(It.IsNotNull<object>())).Returns(true);
It.IsRegex

Określa, że argument typu string musi odpowiadać podanemu wyrażeniu regularnemu.

mockUserService.Setup(service => service.CreateUsername(It.IsRegex("^[a-zA-Z0-9]*$"))).Returns("ValidUsername");
It.IsSubtype

It.IsSubtype<T>() dopasowuje argumenty, które są podtypami typu T.

// Ustawienie zachowania mocka dla metody, która przyjmuje obiekty będące podtypami klasy BaseClass.
mock.Setup(m => m.Execute(It.IsSubtype<BaseClass>())).Returns(true);
It.IsValueType

It.IsValueType dopasowuje dowolną wartość typu wartościowego.

// Ustawienie zachowania mocka dla metody, która przyjmuje dowolny typ wartościowy.
mock.Setup(m => m.Execute(It.IsValueType())).Returns(true);
It.Ref

It.Ref<T>.IsAny jest używane do dopasowania referencji do dowolnego obiektu typu T.

// Ustawienie zachowania mocka dla metody, która przyjmuje referencję do obiektu typu MyType.
mock.Setup(m => m.Execute(ref It.Ref<MyType>.IsAny)).Returns(true);

Metody Verify w Mock

W środowisku testów jednostkowych, biblioteka Moq oferuje szeroki zakres metod Verify, które umożliwiają dokładną weryfikację, jak obiekty mockowe były używane podczas testów. Te metody pozwalają na potwierdzenie, że określone interakcje miały miejsce, co jest kluczowe do zapewnienia, że komponenty aplikacji zachowują się zgodnie z oczekiwaniami. Poniżej przedstawiam szczegółowy opis i przykłady użycia każdej z metod Verify dostępnych w Moq.

Verify

Metoda Verify pozwala sprawdzić, czy określona metoda mocka została wywołana zgodnie z oczekiwaniami.

// Sprawdzenie, czy metoda FetchData została wywołana dokładnie jeden raz
mockDataFetcher.Verify(m => m.FetchData(), Times.Once());
  • m.FetchData(): wywołanie metody FetchData na mocku. Metoda ta powinna być zdefiniowana w interfejsie lub klasie abstrakcyjnej, które mockujemy.
  • Times.Once(): określa, że oczekujemy dokładnie jednego wywołania metody FetchData. Times jest klasą, która pozwala określić, ile razy wywołanie metody powinno mieć miejsce.
VerifyAdd

VerifyAdd jest używany do weryfikacji, czy na mockowanym obiekcie zdarzenie zostało poprawnie dodane.

// Przykład dla klasy z zdarzeniem
public interface IDataService
{
    event EventHandler DataChanged;
}

// Sprawdzenie, czy zdarzenie DataChanged zostało dodane dokładnie jeden raz
mockDataService.VerifyAdd(m => m.DataChanged += It.IsAny<EventHandler>(), Times.Once());
  • m.DataChanged += It.IsAny<EventHandler>(): to operacja dodania subskrypcji do zdarzenia DataChanged. It.IsAny<EventHandler>() oznacza, że dopasowane zostanie dodanie dowolnego obiektu EventHandler, niezależnie od konkretnej instancji.
  • Times.Once(): oznacza, że oczekujemy, iż dodanie subskrypcji zdarzenia DataChanged nastąpi dokładnie jeden raz.
VerifyAll

VerifyAll sprawdza, czy wszystkie skonfigurowane metody na mocku zostały wywołane w sposób zgodny z konfiguracją. Jeśli jakiekolwiek oczekiwanie nie zostało spełnione, test zakończy się niepowodzeniem.

// Ustawienie kilku oczekiwań
mockDataFetcher.Setup(m => m.FetchData()).Returns("Data");
mockDataFetcher.Setup(m => m.SaveData(It.IsAny<string>()));

// Weryfikacja wszystkich ustawień naraz
mockDataFetcher.VerifyAll();
  • m => m.FetchData(): Określa, że ta konfiguracja dotyczy metody FetchData na mockowanym obiekcie.
  • Returns("Data"): Definiuje, że każde wywołanie metody FetchData na tym mocku powinno zwrócić łańcuch znaków "Data". Jest to zachowanie symulowane, które możemy kontrolować w naszym teście.
  • m => m.SaveData(It.IsAny<string>()): Wskazuje, że ta konfiguracja dotyczy metody SaveData, która przyjmuje jeden argument typu string.
  • It.IsAny<string>(): Jest to argument, który mówi, że ta metoda może być wywołana z dowolnym stringiem. It.IsAny<> jest bardzo użyteczne, gdy nie zależy nam na konkretnym wartościach przekazywanych do metody, ale chcemy po prostu upewnić się, że metoda jest wywoływana.
VerifyGet

VerifyGet służy do weryfikacji, że dostęp do właściwości typu get na mocku był wywołany.

// Przykład dla klasy z właściwością
public interface IDataFetcher
{
    string Data { get; }
}

// Sprawdzenie, czy właściwość Data została pobrana przynajmniej raz
mockDataFetcher.VerifyGet(m => m.Data, Times.AtLeastOnce());
  • m.Data: to dostęp do właściwości Data w mockowanym obiekcie. VerifyGet sprawdza, czy ta właściwość została odczytana.
  • Times.AtLeastOnce(): określa, że właściwość Data musi zostać odczytana przynajmniej raz w trakcie testu.
VerifyNoOtherCalls

VerifyNoOtherCalls sprawdza, czy na mocku nie zostały wykonane żadne inne wywołania poza tymi weryfikowanymi.

// Weryfikacja, że nie było żadnych innych wywołań poza zdefiniowanymi
mockDataFetcher.VerifyNoOtherCalls();
VerifyRemove

VerifyRemove używane jest do weryfikacji, że zdarzenie zostało prawidłowo usunięte z mocka.

// Sprawdzenie, czy zdarzenie DataChanged zostało usunięte dokładnie jeden raz
mockDataService.VerifyRemove(m => m.DataChanged -= It.IsAny<EventHandler>(), Times.Once());
  • m.DataChanged -= It.IsAny<EventHandler>(): to operacja usunięcia subskrypcji z zdarzenia DataChanged.
  • It.IsAny<EventHandler>() oznacza, że usunięcie dowolnej subskrypcji zostanie dopasowane.
VerifySet

VerifySet służy do weryfikacji, że właściwość typu set na mocku została ustawiona zgodnie z oczekiwaniami.

// Sprawdzenie, czy właściwość Data została ustawiona na "New Data" dokładnie jeden raz
mockDataFetcher.VerifySet(m => m.Data = "New Data", Times.Once());
  • m.Data = "New Data": to operacja ustawienia wartości właściwości Data na “New Data”. VerifySet weryfikuje, że właściwość została ustawiona zgodnie z oczekiwaniem.

Podsumowanie

W ramach naszej eksploracji biblioteki Moq, szczegółowo omówiliśmy kluczowe narzędzia, które programiści .NET mogą wykorzystać do efektywnego tworzenia mocków w testach jednostkowych. Dzięki metodom oferowanym przez klasę It, testerzy mają możliwość elastycznego dopasowywania argumentów w wywołaniach metod mockowanych obiektów. Metody te, w tym It.IsAny<T>(), It.Is<T>(), It.IsInRange<T>() i inne, umożliwiają precyzyjne określenie warunków, które argumenty muszą spełniać, aby wywołanie metody zostało zaliczone jako dopasowane.

Z kolei metody Verify dostarczane przez Moq pozwalają na dokładne sprawdzenie, czy i jak mockowane obiekty zostały użyte. Verify może być stosowane do potwierdzenia, że określone metody były wywoływane z odpowiednimi argumentami i odpowiednią liczbą razy. Metody takie jak Verify, VerifyAll, VerifyGet, VerifySet, i VerifyNoOtherCalls zapewniają szerokie spektrum możliwości weryfikacji interakcji z mockami, dając deweloperom narzędzia do głębokiej introspekcji zachowań w izolowanych testach jednostkowych.

Przykłady zastosowania tych metod ilustrują, jak można z nich korzystać do budowania solidnych, powtarzalnych i niezawodnych testów jednostkowych. Poprzez zastosowanie tych technik, deweloperzy mogą zapewnić, że ich kod jest nie tylko funkcjonalny, ale także odporny na zmiany i łatwy do utrzymania.

Ostatecznie, połączenie klasy It z metodami Verify oferuje kompleksowe podejście do mockowania w .NET, które jest zarówno potężne, jak i elastyczne. Pozwala to na budowanie skomplikowanych scenariuszy testowych, które są kluczowe dla utrzymania wysokiej jakości i niezawodności nowoczesnych aplikacji software’owych. Ta wiedza i umiejętności są niezbędne dla każdego profesjonalnego programisty .NET dążącego do doskonałości w inżynierii oprogramowania.

Tagi
O Autorze
Tajko
Tajko Tajko z tej strony:) Obecnie pracuję we Wrocławiu przy projektach desktopowych. Skoro sam się czegoś nauczyłem to i inni mogliby nauczyć się tego co ja w łopatologiczny sposób:)

ZOSTAW ODPOWIEDŹ