Hur man enhetstestar statiska metoder i C #

När du bygger eller arbetar i .NET-applikationer kan du ofta använda statiska metoder. Metoder i C # kan vara antingen statiska eller icke-statiska. En icke-statisk metod (även känd som en instansmetod) kan åberopas på en instans av klassen den tillhör. Statiska metoder behöver inte en instans av klassen för att åberopas - de kan kallas på klassen själv.

Även om det är enkelt att testa en icke-statisk metod (åtminstone en som inte kallar en statisk metod eller interagerar med externa beroenden) är det inte lätt att testa en statisk metod. Den här artikeln talar om hur du kan övervinna denna utmaning och testa statiska metoder i C #. 

[Också om: Hur man refaktorerar Guds objekt i C #]

För att arbeta med kodexemplen i den här artikeln bör du ha Visual Studio 2019 installerat i ditt system. Om du inte redan har en kopia kan du ladda ner Visual Studio 2019 här.  

Skapa ett .NET Core-konsolapplikationsprojekt i Visual Studio

Låt oss först skapa ett .NET Core Console Application-projekt i Visual Studio. Förutsatt att Visual Studio 2019 är installerat i ditt system, följ stegen nedan för att skapa ett nytt .NET Core-konsolapplikationsprojekt i Visual Studio.

  1. Starta Visual Studio IDE.
  2. Klicka på "Skapa nytt projekt."
  3. I fönstret "Skapa nytt projekt" väljer du "Konsolapp (.NET Core)" från listan över mallar som visas.
  4. Klicka på Nästa.
  5. I fönstret "Konfigurera ditt nya projekt" som visas nedan anger du namn och plats för det nya projektet.
  6. Klicka på Skapa. 

Detta skapar ett nytt .NET Core-konsolapplikationsprojekt i Visual Studio 2019. På liknande sätt skapar du ytterligare två projekt - ett klassbibliotek och ett enhetstestprojekt (xUnit test). Vi använder dessa tre projekt för att illustrera enhetstestning av statiska metoder i de efterföljande avsnitten i den här artikeln.

När en statisk metod kan och inte kan enhetstestas

Enhetstestning av en statisk metod skiljer sig inte från enhetstestning av en icke-statisk metod. Statiska metoder är inte otestbara i sig. En statisk metod som inte har något tillstånd eller inte ändrar tillstånd kan testas enhet. Så länge metoden och dess beroenden är idempotenta kan metoden enhetstestas. Problemen uppstår när den statiska metoden anropar andra metoder eller när objektet som testas kallar den statiska metoden. Å andra sidan, om objektet som testas kallar en instansmetod, kan du enkelt testa det enkelt.

En statisk metod kan inte enhetstestas om något av följande gäller: 

  • Den statiska metoden interagerar med externa beroenden som en databas, filsystem, nätverk eller extern API.
  • Den statiska metoden innehåller tillståndsinformation, dvs om den cachar data i ett statiskt objekt i klassen.

Tänk på följande kodavsnitt som visar två klasser, nämligen ProductBL och Logger. Medan ProductBL är en icke-statisk klass är Logger en statisk klass. Observera att skrivmetoden för Logger-klassen har anropats från LogMessage-metoden i ProductBL-klassen.

offentlig klass ProductBL

    {

        offentligt ogiltigt LogMessage (strängmeddelande)

        {

            Logger.Write (meddelande);

        }

    }

    offentlig klass Logger

    {

        public static void Write (strängmeddelande)

        {

           // Skriv din kod här för att logga data

        }

    }

Antag att skrivmetoden för Logger-klassen ansluter till en databas och sedan skriver informationen till en databastabell. Namnet på databasen och dess tabell där data ska skrivas kan vara förkonfigurerade i filen appsettings.json. Hur kan du nu skriva enhetstester för ProductBL-metoden?

Observera att statiska metoder inte kan hånas enkelt. Om du till exempel har två klasser med namnet A och B och klass A använder en statisk medlem i klass B, skulle du inte kunna testa klass A isolerat.

Tre sätt att enhetstesta statiska metoder

Du kan använda Moq för att håna icke-statiska metoder men det kan inte användas för att håna om statiska metoder. Även om statiska metoder inte kan hånas enkelt, finns det några sätt att håna av statiska metoder.

Du kan dra nytta av Moles eller Fakes-ramverket från Microsoft för att håna statiska metodsamtal. (Fakes-ramverket ingick i Visual Studio 2012 som efterföljaren till Moles - det är nästa generation av Moles och Stubs.) Ett annat sätt att håna statiska metodsamtal är att använda delegater. Det finns ännu ett sätt att håna statiska metodanrop i en applikation - genom att använda omslagsklasser och beroendeinjektion.

IMHO detta sista alternativ är den bästa lösningen på problemet. Allt du behöver göra är att sätta in det statiska metodanropet i en instansmetod och sedan använda beroendeinjektion för att injicera en instans av omslagsklassen till klassen som testas.

Skapa en omslagsklass i C #

Följande kodavsnitt illustrerar LogWrapper-klassen som implementerar IWrapper-gränssnittet och sveper ett samtal till Logger.Write () -metoden i en instansmetod som heter LogData.

offentlig klass LogWrapper: IWrapper

    {

        sträng _meddelande = null;

        offentlig LogWrapper (strängmeddelande)

        {

            _message = meddelande;

        }

        offentligt ogiltigt LogData (strängmeddelande)

        {

            _message = meddelande;

            Logger.Write (_meddelande);

        }

    }

Följande kodavsnitt visar IWrapper-gränssnittet. Den innehåller deklarationen av LogData-metoden.

offentligt gränssnitt IWrapper

    {

        ogiltig LogData (strängmeddelande);

    }

ProduktBL-klassen använder beroendeinjektion (konstruktionsinjektion) för att injicera en instans av LogWrapper-klassen som visas i kodlistan nedan.

offentlig klass ProductBL

    {

        readonly IWrapper _wrapper;

        statisk sträng _message = null;

        public ProductBL (IWrapper wrapper)

        {

            _omslag = omslag;

        }

        offentligt ogiltigt LogMessage (strängmeddelande)

        {

            _message = meddelande;

            _wrapper.LogData (_meddelande);

        }

    }

LogMessage-metoden för ProductBL-klassen anropar LogData-metoden i förekomsten av LogWrapper-klassen som har injicerats tidigare.

Använd xUnit och Moq för att skapa en enhetstestmetod i C #

Öppna filen UnitTest1.cs och byt namn på klassen UnitTest1 till UnitTestForStaticMethodsDemo. UnitTest1.cs-filerna byttes automatiskt till UnitTestForStaticMethodsDemo.cs. Vi utnyttjar nu Moq-ramverket för att skapa, testa och verifiera mocks.

Följande kodavsnitt illustrerar hur du kan använda Moq-ramverket för att enhetstestmetoder i C #.

var mock = ny Mock ();

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

ny ProductBL (mock.Object) .LogMessage ("Hello World!");

mock.VerifyAll ();

När du utför testet, här är hur utgången ska se ut i Test Explorer-fönstret.

Den fullständiga kodlistan för testklassen ges nedan för din referens.

offentlig klass UnitTestForStaticMethodsDemo

    {

        [Faktum]

        offentligt ogiltigt StaticMethodTest ()

        {

            var mock = ny Mock ();

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

            ny ProductBL (mock.Object) .LogMessage ("Hello World!");

            mock.VerifyAll ();

        }

    }

Enhetstestning är en process som testar kodenheter i en applikation för att kontrollera om de faktiska resultaten från ditt enhetstest matchar de önskade resultaten. Om enhetligt test används kan det hjälpa till att förhindra buggar i utvecklingsfasen av ett projekt.

Statiska metoder kan utgöra ett antal problem när du försöker enhetstesta dem med mocks. Om din applikation kräver att du hånar en statisk metod, bör du överväga att en designlukt - dvs en indikator på en dålig design. Jag kommer att diskutera mocks, förfalskningar och stubbar mer detaljerat i en framtida artikel här.

Hur man gör mer i C #:

  • Hur man refaktorerar Guds objekt i C #
  • Hur man använder ValueTask i C #
  • Hur man använder oföränderlighet i C
  • Hur man använder const, readonly och static i C #
  • Hur man använder dataanmärkningar i C #
  • Hur man arbetar med GUID i C # 8
  • När ska man använda en abstrakt klass mot gränssnitt i C #
  • Hur man arbetar med AutoMapper i C #
  • Hur man använder lambdauttryck i C #
  • Hur man arbetar med Action, Func och Predicate-delegater i C #
  • Hur man arbetar med delegater i C #
  • Hur man implementerar en enkel logger i C #
  • Hur man arbetar med attribut i C #
  • Hur man arbetar med log4net i C #
  • Hur man implementerar förvarets designmönster i C #
  • Hur man arbetar med reflektion i C #
  • Hur man arbetar med filsystemwatcher i C #
  • Hur man utför lat initialisering i C #
  • Hur man arbetar med MSMQ i C #
  • Hur man arbetar med förlängningsmetoder i C #
  • Hur vi lambdauttryck i C #
  • När ska du använda det flyktiga nyckelordet i C #
  • Så här använder du nyckelordet i C #
  • Hur man implementerar polymorfism i C #
  • Hur man bygger din egen uppgiftsschemaläggare i C #
  • Hur man arbetar med RabbitMQ i C #
  • Hur man arbetar med en tuple i C #
  • Utforska virtuella och abstrakta metoder i C #
  • Hur man använder Dapper ORM i C #
  • Hur man använder flygvikt designmönster i C #