Jak używać Moq, aby ułatwić testowanie jednostkowe w C #

Często musimy pisać testy jednostkowe dla kodu, który uzyskuje dostęp do zasobu zewnętrznego, takiego jak baza danych lub system plików. Jeśli takie zasoby nie są dostępne, jedynym sposobem na zapewnienie możliwości wykonania testów jest utworzenie pozorowanych obiektów. Zasadniczo, korzystając z fałszywych implementacji tych podstawowych zależności, można przetestować interakcję między testowaną metodą a jej zależnościami. Trzy najpopularniejsze frameworki do makietowania dla programistów .Net to Rhino Mocks, Moq i NMock.

Wśród nich Moq może być najbardziej elastycznym i łatwym w użyciu. Framework Moq zapewnia elegancki sposób konfigurowania, testowania i weryfikowania makiet. W tym artykule omówiono Moq i sposób, w jaki można go użyć do izolowania jednostek kodu od ich zależności.

Pierwsze kroki z Moq

Możesz użyć Moq do tworzenia pozorowanych obiektów, które symulują lub naśladują rzeczywisty obiekt. Moq może służyć do mockowania zarówno klas, jak i interfejsów. Jest jednak kilka ograniczeń, o których powinieneś wiedzieć. Klasy, które mają być mockowane, nie mogą być statyczne ani zapieczętowane, a mockowana metoda powinna być oznaczona jako wirtualna. (Należy pamiętać, że istnieją obejścia tych ograniczeń. Można na przykład pozorować metodę statyczną, korzystając z wzorca projektowego adaptera).

Pierwszym krokiem w korzystaniu z Moq jest zainstalowanie go, aby można go było używać w projekcie testu jednostkowego. Możesz pobrać Moq z GitHub i odpowiednio dodać odniesienia. Jednak wolę instalować Moq za pośrednictwem NuGet, ponieważ jest to łatwiejsze i mniej prawdopodobne, że pominiesz odniesienia. Możesz zainstalować Moq za pomocą następującego polecenia w wierszu polecenia NuGet.

Install-Package Moq

Jak mockować interfejsy za pomocą Moq

Zacznijmy od kpiny z interfejsu. Składnia tworzenia obiektu pozorowanego przy użyciu klasy Mock jest podana poniżej.

Mock mockObjectType = new Mock ();

Rozważmy teraz następujący interfejs o nazwie IAuthor.

interfejs publiczny IAuthor

    {

        int Id {get; zestaw; }

        string FirstName {get; zestaw; }

        string LastName {get; zestaw; }

    }

Korzystając ze struktury Moq, możesz utworzyć obiekt pozorowany, ustawić wartości właściwości, określić parametry i zwrócić wartości w wywołaniach metod. Poniższy fragment kodu ilustruje, jak można utworzyć wystąpienie z interfejsu IAuthor przy użyciu Moq.

var mock = new Mock ();

Zauważ, że klasa Mock należy do frameworka Moq i zawiera generyczny konstruktor, który akceptuje typ interfejsu, który chcesz utworzyć. Moq korzysta z wyrażeń lambda, delegatów i typów ogólnych. Wszystko to sprawia, że ​​korzystanie z frameworka jest bardzo intuicyjne.

Poniższy fragment kodu pokazuje, jak można mockować interfejs IAuthor i udostępniać właściwościom mockowanej instancji odpowiednie wartości. Zwróć uwagę, jak używamy Assert do weryfikowania wartości właściwości mockowanej instancji.

var author = new Mock ();

author.SetupGet (p => p.Id) .Returns (1);

author.SetupGet (p => p.FirstName) .Returns („Joydip”);

author.SetupGet (p => p.LastName) .Returns („Kanjilal”);

Assert.AreEqual („Joydip”, author.Object.FirstName);

Assert.AreEqual („Kanjilal”, autor.Obiekt.LastName);

Jak mockować metody przy użyciu Moq

Rozważmy teraz następującą klasę o nazwie Article. Klasa Article zawiera tylko jedną metodę o nazwie GetPublicationDate, która akceptuje identyfikator artykułu jako parametr i zwraca datę publikacji artykułu.

klasa publiczna Art

    {

        publiczne wirtualne DateTime GetPublicationDate (int articleId)

        {

            wyrzucić nowy NotImplementedException ();

        }

    }

Ponieważ metoda GetPublicationDate nie została jeszcze zaimplementowana w klasie Article, metoda została wyszydzona w celu zwrócenia bieżącej daty jako daty publikacji, jak pokazano we fragmencie kodu podanym poniżej.

var mockObj = new Mock ();
mockObj.Setup (x => x.GetPublicationDate (It.IsAny ())). Returns ((int x) => DateTime.Now);

Metoda Setup służy do definiowania zachowania metody, która jest przekazywana do niej jako parametr. W tym przykładzie służy do definiowania zachowania metody GetPublicationDate. Wywołanie do It.IsAny()oznacza, że ​​metoda GetPublicationDate zaakceptuje parametr typu integer; Itodnosi się do klasy statycznej. Metoda Returns służy do określenia wartości zwracanej metody określonej w wywołaniu metody Setup. W tym przykładzie metoda Returns służy do określenia wartości zwracanej metody jako bieżącej daty systemowej.

Moq pozwala sprawdzić, czy została wywołana określona metoda lub właściwość. Ilustruje to poniższy fragment kodu.

mockObj.Verify (t => t.GetPublicationDate (It.IsAny ()));

Tutaj używamy metody Verify, aby określić, czy GetPublicationDate została wywołana w obiekcie makiety.

Jak mockować metody klasy bazowej przy użyciu Moq

Rozważmy następujący fragment kodu. Mamy tutaj dwie klasy - klasę RepositoryBase i klasę AuthorRepository, która ją rozszerza.

publiczna klasa abstrakcyjna RepositoryBase

{

    publiczne wirtualne bool IsServiceConnectionValid ()

    {

        // Jakiś kod

    }

}

public class AuthorRepository: RepositoryBase

{

    public void Zapisz ()

    {

        if (IsServiceConnectionValid ())

        {

            // Jakiś kod

        }

    }

}

Teraz przypuśćmy, że chcemy sprawdzić, czy połączenie z bazą danych jest prawidłowe. Jednak możemy nie chcieć testować całego kodu wewnątrz metody IsServiceConnectionValid. Na przykład metoda IsServiceConnectionValid może zawierać kod odnoszący się do biblioteki innej firmy. Nie chcielibyśmy tego testować, prawda? Tutaj z pomocą przychodzi metoda CallBase w Moq. 

W takich sytuacjach, w których masz metodę w klasie bazowej, która została zastąpiona w typie symulowanym i musisz tylko mockować podstawową wersję metody zastępowanej, możesz rysować na CallBase. Poniższy fragment kodu pokazuje, jak można utworzyć częściowy obiekt makiety klasy AuthorRepository, ustawiając właściwość CallBase na true.

var mockObj = new Mock () {CallBase = true};

mockObj.Setup (x => x.IsServiceConnectionValid ()). Returns (true);

Struktura Moq ułatwia tworzenie pozorowanych obiektów, które naśladują zachowanie klas i interfejsów do testowania, przy użyciu tylko potrzebnych funkcji. Aby uzyskać więcej informacji na temat testowania przy użyciu makiet, zapoznaj się z tym świetnym artykułem Martina Fowlera.