Hur man använder Moq för att underlätta enhetstester i C #

Vi behöver ofta skriva enhetstester för kod som får åtkomst till en extern resurs, till exempel en databas eller ett filfilsystem. Om sådana resurser inte är tillgängliga är det enda sättet att säkerställa att testerna kan utföras genom att skapa mock-objekt. I grund och botten, genom att använda falska implementeringar av dessa underliggande beroenden, kan du testa samspelet mellan metoden som testas och dess beroenden. Tre av de mest populära spottramarna för .Net-utvecklare är Rhino Mocks, Moq och NMock.

Bland dessa kan Moq vara den mest flexibla och lättanvända. Moq-ramverket ger ett elegant sätt att skapa, testa och verifiera mocks. Den här artikeln presenterar en diskussion om Moq och hur den kan användas för att isolera kodenheter från deras beroenden.

Komma igång med Moq

Du kan använda Moq för att skapa mock-objekt som simulerar eller härmar ett riktigt objekt. Moq kan användas för att håna både klasser och gränssnitt. Det finns dock några begränsningar du bör vara medveten om. Klasserna som ska hånas kan inte vara statiska eller förseglade, och metoden som hånas bör markeras som virtuell. (Observera att det finns lösningar på dessa begränsningar. Du kan håna en statisk metod genom att till exempel utnyttja adapterens designmönster.)

Det första steget i att använda Moq är att installera den så att du kan använda den i ditt enhetstestprojekt. Du kan ladda ner Moq från GitHub och lägga till referenser efter behov. Jag föredrar dock att installera Moq via NuGet eftersom det är både lättare och mindre benägna att sakna referenser. Du kan installera Moq med hjälp av följande kommando på NuGet-kommandoraden.

Install-Package Moq

Hur man mockar gränssnitt med Moq

Låt oss börja med att håna ett gränssnitt. Syntaxen för att skapa ett mock-objekt med Mock-klassen ges nedan.

Mock mockObjectType = ny Mock ();

Tänk nu på följande gränssnitt som heter IAuthor.

offentligt gränssnitt IAuthor

    {

        int Id {get; uppsättning; }

        sträng Förnamn {get; uppsättning; }

        string LastName {get; uppsättning; }

    }

Med hjälp av Moq-ramverket kan du skapa ett mock-objekt, ställa in egenskapsvärden, ange parametrar och returnera värden på metodanropen. Följande kodavsnitt illustrerar hur du kan skapa en instans från IAuthor-gränssnittet med Moq.

var mock = ny Mock ();

Observera att Mock-klassen tillhör Moq-ramverket och innehåller en generisk konstruktör som accepterar den typ av gränssnitt du vill skapa. Moq utnyttjar lambdauttryck, delegater och generika. Allt detta gör att ramverket är mycket intuitivt.

Följande kodavsnitt visar hur du kan håna IAuthor-gränssnittet och förse egenskaperna för den hånade instansen med lämpliga värden. Observera hur vi använder Assert för att verifiera värdena för egenskaperna för den hånade instansen.

var författare = ny 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”, författare.Object.FirstName);

Assert.AreEqual (“Kanjilal”, författare.Object.LastName);

Hur man hånar metoder med Moq

Låt oss nu överväga följande klass som heter Article. Artikelklassen innehåller bara en metod som heter GetPublicationDate som accepterar ett artikel-ID som parameter och returnerar publiceringsdatumet för artikeln.

public class-artikel

    {

        offentlig virtuell DateTime GetPublicationDate (int articleId)

        {

            kasta nya NotImplementedException ();

        }

    }

Eftersom GetPublicationDate-metoden ännu inte är implementerad i artikelklassen har metoden hånats för att återge det aktuella datumet som publiceringsdatum, som visas i kodavsnittet nedan.

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

Metoden Setup används för att definiera beteendet hos en metod som skickas till den som en parameter. I det här exemplet används den för att definiera beteendet hos GetPublicationDate-metoden. Uppmaningen till It.IsAny()innebär att GetPublicationDate-metoden accepterar en parameter av typen heltal; Itavser en statisk klass. Metoden Returer används för att ange returvärdet för metoden som anges i installationsmetodsanropet. I det här exemplet används metoden Retur för att ange metodens returvärde som det aktuella systemdatumet.

Moq låter dig verifiera om en viss metod eller en egendom anropades. Följande kodavsnitt illustrerar detta.

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

Här använder vi verifieringsmetoden för att avgöra om GetPublicationDate anropades till det mock-objektet.

Hur man hånar basklassmetoder med Moq

Tänk på följande kod. Vi har två klasser här - RepositoryBase-klassen och AuthorRepository-klassen som utökar den.

offentlig abstrakt klass RepositoryBase

{

    offentlig virtuell bool IsServiceConnectionValid ()

    {

        // Någon kod

    }

}

public class AuthorRepository: RepositoryBase

{

    offentligt tomrum Spara ()

    {

        om (IsServiceConnectionValid ())

        {

            // Någon kod

        }

    }

}

Antag nu att vi vill kontrollera om databasanslutningen är giltig. Vi kanske dock inte vill testa all kod i IsServiceConnectionValid-metoden. Exempelvis kan metoden IsServiceConnectionValid innehålla kod som gäller ett tredjepartsbibliotek. Vi skulle inte vilja testa det, eller hur? Här kommer CallBase-metoden i Moq till undsättning. 

I situationer som denna, där du har en metod i basklassen som har åsidosatts i den spottade typen, och du bara behöver spotta basversionen av den åsidosatta metoden, kan du använda CallBase. Följande kodavsnitt visar hur du kan skapa ett partiellt mock-objekt av klassen AuthorRepository genom att ställa in egenskapen CallBase till true.

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

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

Moq-ramverket gör det enkelt att skapa mock-objekt som efterliknar beteendet hos klasser och gränssnitt för testning, med precis den funktionalitet du behöver. För mer om test med mocks, kolla in den här fantastiska artikeln från Martin Fowler.