Jak testować jednostkowe metody statyczne w C #

Podczas tworzenia lub pracy w aplikacjach .NET często można używać metod statycznych. Metody w C # mogą być statyczne lub niestatyczne. Metoda niestatyczna (znana również jako metoda instancji) może zostać wywołana na instancji klasy, do której należy. Metody statyczne nie wymagają wywołania instancji klasy - można je wywołać z samej klasy.

Chociaż testowanie metody niestatycznej (przynajmniej takiej, która nie wywołuje metody statycznej ani nie wchodzi w interakcję z zewnętrznymi zależnościami) jest proste, testowanie metody statycznej wcale nie jest łatwym zadaniem. W tym artykule omówiono, w jaki sposób można pokonać to wyzwanie i przetestować metody statyczne w języku C #. 

[Także na: Jak refaktoryzować obiekty Boga w C #]

Aby pracować z przykładami kodu przedstawionymi w tym artykule, w systemie powinien być zainstalowany program Visual Studio 2019. Jeśli nie masz jeszcze kopii, możesz pobrać program Visual Studio 2019 tutaj.  

Utwórz projekt aplikacji konsoli .NET Core w programie Visual Studio

Po pierwsze, utwórzmy projekt aplikacji .NET Core Console w programie Visual Studio. Zakładając, że w systemie jest zainstalowany program Visual Studio 2019, wykonaj czynności opisane poniżej, aby utworzyć nowy projekt aplikacji konsoli .NET Core w programie Visual Studio.

  1. Uruchom środowisko IDE programu Visual Studio.
  2. Kliknij „Utwórz nowy projekt”.
  3. W oknie „Utwórz nowy projekt” wybierz „Aplikacja konsoli (.NET Core)” z wyświetlonej listy szablonów.
  4. Kliknij Następny.
  5. W oknie „Konfiguruj nowy projekt” pokazanym obok podaj nazwę i lokalizację nowego projektu.
  6. Kliknij Utwórz. 

Spowoduje to utworzenie nowego projektu aplikacji konsoli .NET Core w programie Visual Studio 2019. W podobny sposób utwórz jeszcze dwa projekty - bibliotekę klas i projekt testów jednostkowych (test xUnit). Użyjemy tych trzech projektów do zilustrowania testów jednostkowych metod statycznych w kolejnych sekcjach tego artykułu.

Kiedy metoda statyczna może i nie może być testowana jednostkowo

Testowanie jednostkowe metodą statyczną nie różni się od testowania jednostkowego metodą niestatyczną. Metody statyczne same w sobie nie są niesprawne. Metoda statyczna, która nie ma stanu lub nie zmienia stanu, może zostać przetestowana jednostkowo. Dopóki metoda i jej zależności są idempotentne, metodę można przetestować jednostkowo. Problemy pojawiają się, gdy metoda statyczna wywołuje inne metody lub gdy testowany obiekt wywołuje metodę statyczną. Z drugiej strony, jeśli testowany obiekt wywołuje metodę instancji, można ją łatwo przetestować jednostkowo.

Metoda statyczna nie może być testowana jednostkowo, jeśli spełniony jest którykolwiek z poniższych warunków: 

  • Metoda statyczna współdziała z zależnościami zewnętrznymi, takimi jak baza danych, system plików, sieć lub zewnętrzny interfejs API.
  • Metoda statyczna przechowuje informacje o stanie, tj. Jeśli buforuje dane w statycznym obiekcie klasy.

Rozważmy następujący fragment kodu, który przedstawia dwie klasy, a mianowicie ProductBL i Logger. Podczas gdy ProductBL jest klasą niestatyczną, Logger jest klasą statyczną. Należy zauważyć, że metoda Write klasy Logger została wywołana z metody LogMessage klasy ProductBL.

klasa publiczna ProductBL

    {

        public void LogMessage (wiadomość tekstowa)

        {

            Logger.Write (wiadomość);

        }

    }

    Public Class Logger

    {

        public static void Write (wiadomość tekstowa)

        {

           // Wpisz tutaj swój kod, aby rejestrować dane

        }

    }

Załóżmy, że metoda Write klasy Logger łączy się z bazą danych, a następnie zapisuje dane w tabeli bazy danych. Nazwa bazy danych i jej tabela, w której mają zostać zapisane dane, mogą być wstępnie skonfigurowane w pliku appsettings.json. Jak możesz teraz pisać testy jednostkowe dla metody ProductBL?

Zwróć uwagę, że metody statyczne nie mogą być łatwo wyszydzane. Na przykład, jeśli masz dwie klasy o nazwach A i B, a klasa A używa statycznego elementu członkowskiego klasy B, nie będziesz w stanie testować jednostkowo klasy A w izolacji.

Trzy sposoby testowania jednostkowego metod statycznych

Możesz użyć Moq do mockowania metod niestatycznych, ale nie można go używać do mockowania metod statycznych. Chociaż metody statyczne nie mogą być łatwo wyszydzane, istnieje kilka sposobów na pozorowanie metod statycznych.

Możesz skorzystać z platformy Moles lub Fakes firmy Microsoft, aby udawać wywołania metod statycznych. (Framework Fakes został dołączony do Visual Studio 2012 jako następca Molesów - jest to następna generacja Moli i Stubów). Innym sposobem na symulowanie wywołań metod statycznych jest użycie delegatów. Istnieje jeszcze inny sposób na mockowanie wywołań metod statycznych w aplikacji - przy użyciu klas opakowania i iniekcji zależności.

IMHO ta ostatnia opcja jest najlepszym rozwiązaniem problemu. Wszystko, co musisz zrobić, to zawinąć wywołanie metody statycznej wewnątrz metody instancji, a następnie użyć iniekcji zależności w celu wstrzyknięcia instancji klasy opakowania do testowanej klasy.

Utwórz klasę otoki w C #

Poniższy fragment kodu ilustruje klasę LogWrapper, która implementuje interfejs IWrapper i opakowuje wywołanie metody Logger.Write () wewnątrz metody wystąpienia o nazwie LogData.

publiczna klasa LogWrapper: IWrapper

    {

        string _message = null;

        public LogWrapper (wiadomość tekstowa)

        {

            _message = wiadomość;

        }

        public void LogData (wiadomość tekstowa)

        {

            _message = wiadomość;

            Logger.Write (_message);

        }

    }

Poniższy fragment kodu przedstawia interfejs IWrapper. Zawiera deklarację metody LogData.

publiczny interfejs IWrapper

    {

        void LogData (komunikat tekstowy);

    }

Klasa ProductBL używa iniekcji zależności (iniekcji konstruktora) w celu wstrzyknięcia wystąpienia klasy LogWrapper, jak pokazano na liście kodu podanej poniżej.

klasa publiczna ProductBL

    {

        readonly IWrapper _wrapper;

        ciąg statyczny _message = null;

        public ProductBL (opakowanie IWrapper)

        {

            _wrapper = wrapper;

        }

        public void LogMessage (wiadomość tekstowa)

        {

            _message = wiadomość;

            _wrapper.LogData (_message);

        }

    }

Metoda LogMessage klasy ProductBL wywołuje metodę LogData w wystąpieniu klasy LogWrapper, które zostało wcześniej wstrzyknięte.

Użyj xUnit i Moq, aby utworzyć metodę testów jednostkowych w C #

Otwórz plik UnitTest1.cs i zmień nazwę klasy UnitTest1 na UnitTestForStaticMethodsDemo. Nazwy plików UnitTest1.cs zostałyby automatycznie zmienione na UnitTestForStaticMethodsDemo.cs. Skorzystamy teraz z platformy Moq, aby skonfigurować, przetestować i zweryfikować makiety.

Poniższy fragment kodu ilustruje, jak można używać struktury Moq do testowania jednostkowego metod w języku C #.

var mock = new Mock ();

mock.Setup (x => x.LogData (It.IsAny ()));

nowy ProductBL (mock.Object) .LogMessage ("Witaj świecie!");

mock.VerifyAll ();

Oto jak powinny wyglądać dane wyjściowe podczas wykonywania testu w oknie Eksploratora testów.

Pełną listę kodów klas testowych podano poniżej w celach informacyjnych.

publiczna klasa UnitTestForStaticMethodsDemo

    {

        [Fakt]

        public void StaticMethodTest ()

        {

            var mock = new Mock ();

            mock.Setup (x => x.LogData (It.IsAny ()));

            nowy ProductBL (mock.Object) .LogMessage ("Witaj świecie!");

            mock.VerifyAll ();

        }

    }

Testowanie jednostkowe to proces, który testuje jednostki kodu w aplikacji w celu sprawdzenia, czy rzeczywiste wyniki testu jednostkowego są zgodne z oczekiwanymi wynikami. Rozsądnie stosowane testy jednostkowe mogą pomóc w zapobieganiu błędom w fazie rozwoju projektu.

Metody statyczne mogą stwarzać wiele problemów podczas próby ich testowania jednostkowego przy użyciu makiet. Jeśli Twoja aplikacja wymaga, abyś kpił z metody statycznej, powinieneś wziąć pod uwagę zapach projektu - tj. Wskaźnik złego projektu. Omówię bardziej szczegółowo kpiny, podróbki i niedopałki w przyszłym artykule tutaj.

Jak zrobić więcej w C #:

  • Jak refaktoryzować obiekty Boga w C #
  • Jak używać funkcji ValueTask w C #
  • Jak używać niezmienności w C
  • Jak używać const, readonly i static w C #
  • Jak używać adnotacji danych w C #
  • Jak pracować z identyfikatorami GUID w C # 8
  • Kiedy używać klasy abstrakcyjnej, a kiedy interfejsu w języku C #
  • Jak pracować z AutoMapper w C #
  • Jak używać wyrażeń lambda w C #
  • Jak pracować z delegatami Action, Func i Predicate w C #
  • Jak pracować z delegatami w C #
  • Jak zaimplementować prosty rejestrator w C #
  • Jak pracować z atrybutami w C #
  • Jak pracować z log4net w C #
  • Jak zaimplementować wzorzec projektu repozytorium w C #
  • Jak pracować z odbiciem w C #
  • Jak pracować z Filesystemwatcher w C #
  • Jak wykonać leniwą inicjalizację w C #
  • Jak pracować z MSMQ w C #
  • Jak pracować z metodami rozszerzającymi w C #
  • Jak nam wyrażenia lambda w C #
  • Kiedy używać słowa kluczowego volatile w języku C #
  • Jak używać słowa kluczowego yield w języku C #
  • Jak zaimplementować polimorfizm w C #
  • Jak zbudować własny harmonogram zadań w C #
  • Jak pracować z RabbitMQ w C #
  • Jak pracować z krotką w C #
  • Eksplorowanie metod wirtualnych i abstrakcyjnych w C #
  • Jak korzystać z Dapper ORM w C #
  • Jak używać wzorca projektowego flyweight w C #