Come usare Moq per facilitare i test di unità in C #

Spesso è necessario scrivere unit test per il codice che accede a una risorsa esterna come un database o un file file system. Se tali risorse non sono disponibili, l'unico modo per garantire che i test possano essere eseguiti è creare oggetti fittizi. In sostanza, attingendo a false implementazioni di queste dipendenze sottostanti, è possibile testare l'interazione tra il metodo testato e le sue dipendenze. Tre dei framework di mocking più popolari per gli sviluppatori .Net sono Rhino Mocks, Moq e NMock.

Tra questi, Moq può essere il più flessibile e facile da usare. Il framework Moq fornisce un modo elegante per impostare, testare e verificare i mock. Questo articolo presenta una discussione su Moq e su come può essere utilizzato per isolare unità di codice dalle loro dipendenze.

Iniziare con Moq

Puoi usare Moq per creare oggetti fittizi che simulano o imitano un oggetto reale. Moq può essere utilizzato per deridere sia le classi che le interfacce. Tuttavia, ci sono alcune limitazioni di cui dovresti essere a conoscenza. Le classi da prendere in giro non possono essere statiche o sigillate e il metodo da prendere in giro deve essere contrassegnato come virtuale. (Nota che ci sono soluzioni alternative a queste restrizioni. Potresti simulare un metodo statico sfruttando il modello di progettazione dell'adattatore, ad esempio.)

Il primo passaggio nell'utilizzo di Moq è installarlo in modo da poterlo utilizzare nel progetto di unit test. Puoi scaricare Moq da GitHub e aggiungere riferimenti come appropriato. Tuttavia, preferisco installare Moq tramite NuGet perché è più facile e meno probabile che manchi i riferimenti. È possibile installare Moq usando il comando seguente nella riga di comando di NuGet.

Install-Package Moq

Come simulare le interfacce usando Moq

Cominciamo prendendo in giro un'interfaccia. La sintassi per la creazione di un oggetto fittizio utilizzando la classe Mock è fornita di seguito.

Mock mockObjectType = new Mock ();

Ora, considera la seguente interfaccia denominata IAuthor.

interfaccia pubblica IAuthor

    {

        int Id {get; impostato; }

        stringa FirstName {get; impostato; }

        stringa LastName {get; impostato; }

    }

Utilizzando il framework Moq, è possibile creare un oggetto fittizio, impostare valori di proprietà, specificare parametri e restituire valori nelle chiamate al metodo. Il frammento di codice seguente illustra come creare un'istanza dall'interfaccia IAuthor utilizzando Moq.

var mock = new Mock ();

Si noti che la classe Mock appartiene al framework Moq e contiene un costruttore generico che accetta il tipo di interfaccia che si desidera creare. Moq sfrutta le espressioni lambda, i delegati e i generici. Tutto ciò rende l'utilizzo del framework molto intuitivo.

Il frammento di codice seguente mostra come è possibile simulare l'interfaccia IAuthor e fornire le proprietà dell'istanza derisa con i valori appropriati. Nota come utilizziamo Assert per verificare i valori delle proprietà dell'istanza derisa.

var autore = 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", author.Object.LastName);

Come simulare i metodi usando Moq

Consideriamo ora la seguente classe denominata Article. La classe Article contiene un solo metodo chiamato GetPublicationDate che accetta un ID articolo come parametro e restituisce la data di pubblicazione dell'articolo.

articolo di classe pubblica

    {

        DateTime virtuale pubblico GetPublicationDate (int articleId)

        {

            lancia una nuova NotImplementedException ();

        }

    }

Poiché il metodo GetPublicationDate non è ancora implementato nella classe Article, il metodo è stato deriso per restituire la data corrente come data di pubblicazione, come mostrato nello snippet di codice riportato di seguito.

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

Il metodo Setup viene utilizzato per definire il comportamento di un metodo che gli viene passato come parametro. In questo esempio, viene utilizzato per definire il comportamento del metodo GetPublicationDate. La chiamata a It.IsAny()implica che il metodo GetPublicationDate accetterà un parametro di tipo integer; Itsi riferisce a una classe statica. Il metodo Returns viene utilizzato per specificare il valore restituito del metodo specificato nella chiamata al metodo Setup. In questo esempio, il metodo Returns viene utilizzato per specificare il valore restituito del metodo come data di sistema corrente.

Moq consente di verificare se è stato chiamato un particolare metodo o una proprietà. Lo snippet di codice seguente lo illustra.

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

Qui stiamo usando il metodo Verify per determinare se GetPublicationDate è stato chiamato sull'oggetto fittizio.

Come deridere i metodi della classe base usando Moq

Considera la seguente parte di codice. Abbiamo due classi qui: la classe RepositoryBase e la classe AuthorRepository che la estende.

classe astratta pubblica RepositoryBase

{

    bool virtuale pubblico IsServiceConnectionValid ()

    {

        // Un po 'di codice

    }

}

classe pubblica AuthorRepository: RepositoryBase

{

    public void Save ()

    {

        if (IsServiceConnectionValid ())

        {

            // Un po 'di codice

        }

    }

}

Supponiamo ora di voler controllare se la connessione al database è valida. Tuttavia, potremmo non voler testare tutto il codice all'interno del metodo IsServiceConnectionValid. Ad esempio, il metodo IsServiceConnectionValid potrebbe contenere codice relativo a una libreria di terze parti. Non vorremmo testarlo, giusto? Qui è dove il metodo CallBase in Moq viene in soccorso. 

In situazioni come questa, in cui hai un metodo nella classe base che è stato sovrascritto nel tipo deriso e devi prendere in giro solo la versione base del metodo sovrascritto, puoi disegnare su CallBase. Il frammento di codice seguente mostra come creare un oggetto fittizio parziale della classe AuthorRepository impostando la proprietà CallBase su true.

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

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

Il framework Moq semplifica la creazione di oggetti fittizi che imitano il comportamento di classi e interfacce per i test, con solo le funzionalità necessarie. Per ulteriori informazioni sui test con i mock, controlla questo fantastico articolo di Martin Fowler.