Comment utiliser Moq pour faciliter les tests unitaires en C #

Nous avons souvent besoin d'écrire des tests unitaires pour le code qui accède à une ressource externe telle qu'une base de données ou un système de fichiers de fichiers. Si ces ressources ne sont pas disponibles, la seule façon de garantir que les tests peuvent s'exécuter est de créer des objets fictifs. En gros, en vous basant sur de fausses implémentations de ces dépendances sous-jacentes, vous pouvez tester l'interaction entre la méthode testée et ses dépendances. Trois des frameworks moqueurs les plus populaires pour les développeurs .Net sont Rhino Mocks, Moq et NMock.

Parmi ceux-ci, Moq est peut-être le plus flexible et le plus facile à utiliser. Le framework Moq fournit un moyen élégant de configurer, tester et vérifier les simulations. Cet article présente une discussion de Moq et comment il peut être utilisé pour isoler des unités de code de leurs dépendances.

Premiers pas avec Moq

Vous pouvez utiliser Moq pour créer des objets fictifs qui simulent ou imitent un objet réel. Moq peut être utilisé pour simuler les classes et les interfaces. Cependant, il y a quelques limitations dont vous devez être conscient. Les classes à simuler ne peuvent pas être statiques ou scellées, et la méthode simulée doit être marquée comme virtuelle. (Notez qu'il existe des solutions de contournement à ces restrictions. Vous pouvez vous moquer d'une méthode statique en tirant parti du modèle de conception de l'adaptateur, par exemple.)

La première étape de l'utilisation de Moq consiste à l'installer afin de pouvoir l'utiliser dans votre projet de test unitaire. Vous pouvez télécharger Moq depuis GitHub et ajouter des références le cas échéant. Cependant, je préfère installer Moq via NuGet car il est à la fois plus facile et moins susceptible de manquer des références. Vous pouvez installer Moq à l'aide de la commande suivante sur la ligne de commande NuGet.

Install-Package Moq

Comment simuler des interfaces à l'aide de Moq

Commençons par nous moquer d'une interface. La syntaxe de création d'un objet fictif à l'aide de la classe Mock est donnée ci-dessous.

Mock mockObjectType = nouveau Mock ();

Maintenant, considérez l'interface suivante nommée IAuthor.

interface publique IAuthor

    {

        int Id {get; ensemble; }

        string FirstName {get; ensemble; }

        string LastName {get; ensemble; }

    }

À l'aide de l'infrastructure Moq, vous pouvez créer un objet fictif, définir des valeurs de propriété, spécifier des paramètres et renvoyer des valeurs lors des appels de méthode. L'extrait de code suivant montre comment créer une instance à partir de l'interface IAuthor à l'aide de Moq.

var mock = nouveau Mock ();

Notez que la classe Mock appartient au framework Moq et contient un constructeur générique qui accepte le type d'interface que vous souhaitez créer. Moq tire parti des expressions lambda, des délégués et des génériques. Tout cela rend l'utilisation du framework très intuitive.

L'extrait de code suivant montre comment vous pouvez simuler l'interface IAuthor et fournir les propriétés de l'instance simulée avec les valeurs appropriées. Notez comment nous utilisons Assert pour vérifier les valeurs des propriétés de l'instance simulée.

var auteur = nouveau 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);

Comment se moquer des méthodes en utilisant Moq

Considérons maintenant la classe suivante nommée Article. La classe Article contient une seule méthode appelée GetPublicationDate qui accepte un ID d'article comme paramètre et renvoie la date de publication de l'article.

Article de classe publique

    {

        public virtual DateTime GetPublicationDate (int articleId)

        {

            throw new NotImplementedException ();

        }

    }

Étant donné que la méthode GetPublicationDate n'est pas encore implémentée dans la classe Article, la méthode a été simulée pour renvoyer la date actuelle comme date de publication, comme indiqué dans l'extrait de code ci-dessous.

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

La méthode Setup est utilisée pour définir le comportement d'une méthode qui lui est transmise en tant que paramètre. Dans cet exemple, il est utilisé pour définir le comportement de la méthode GetPublicationDate. L'appel à It.IsAny()implique que la méthode GetPublicationDate acceptera un paramètre de type integer; Itfait référence à une classe statique. La méthode Returns est utilisée pour spécifier la valeur de retour de la méthode spécifiée dans l'appel de méthode Setup. Dans cet exemple, la méthode Returns est utilisée pour spécifier la valeur de retour de la méthode comme date système actuelle.

Moq vous permet de vérifier si une méthode ou une propriété particulière a été appelée. L'extrait de code suivant illustre cela.

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

Ici, nous utilisons la méthode Verify pour déterminer si GetPublicationDate a été appelée sur l'objet fictif.

Comment simuler des méthodes de classe de base à l'aide de Moq

Considérez le morceau de code suivant. Nous avons ici deux classes: la classe RepositoryBase et la classe AuthorRepository qui l'étend.

classe abstraite publique RepositoryBase

{

    public virtuel bool IsServiceConnectionValid ()

    {

        // Un peu de code

    }

}

classe publique AuthorRepository: RepositoryBase

{

    public void Save ()

    {

        if (IsServiceConnectionValid ())

        {

            // Un peu de code

        }

    }

}

Supposons maintenant que nous voulions vérifier si la connexion à la base de données est valide. Cependant, nous ne souhaitons peut-être pas tester tout le code de la méthode IsServiceConnectionValid. Par exemple, la méthode IsServiceConnectionValid peut contenir du code appartenant à une bibliothèque tierce. Nous ne voudrions pas tester cela, non? C'est ici que la méthode CallBase de Moq vient à la rescousse. 

Dans des situations comme celle-ci, où vous avez une méthode dans la classe de base qui a été remplacée dans le type simulé et que vous devez simuler uniquement la version de base de la méthode remplacée, vous pouvez dessiner sur CallBase. L'extrait de code suivant montre comment créer un objet fictif partiel de la classe AuthorRepository en définissant la propriété CallBase sur true.

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

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

Le framework Moq facilite la création d'objets fictifs qui imitent le comportement des classes et des interfaces pour les tests, avec uniquement les fonctionnalités dont vous avez besoin. Pour en savoir plus sur les tests avec des simulacres, consultez cet excellent article de Martin Fowler.