Mocking avec le framework « Moq » en .NET

Lorsque l’on doit écrire des tests unitaires, il est souvent très pratique de pouvoir simuler le comportement de certains objets pour travailler dans un contexte isolé.

Par exemple simuler un « repository » d’objets habituellement liés à la base de données avec un « repository » factice ne retournant qu’un objet bien défini ou encore simuler les réponses d’un service Web sans même devoir le contacter.

Il est aussi important d’utiliser des objets factices pour conserver le caractère de répétabilité d’un test (une propriété de date ou un capteur de température devant rester figés pour obtenir les mêmes résultats en sortie et donc la validation du test).

Bien sûr il est facile de créer soi même ce type d’objet factice (que l’on pourrait nommer « stub » ou encore « bouchon »), mais pour nous faciliter le travail il existe de nombreux frameworks dédiés à cette tâche dite du « mocking ».

Un mock est un remplacement orienté test d’un objet. Il possède en plus d’un « stub » classique la capacité de se paramétrer à l’exécution (techniquement il génère souvent un proxy dynamique) et de vérifier qu’il est utilisé comme attendu.

Le mock est comparable au mannequin utilisé dans les crashs tests de véhicules par exemple. C’est un objet factice qui présente des propriétés pertinentes dans le cadre du test (ici une taille et une masse comparable à celles des êtres humains).

Il faut bien comprendre que ce n’est pas le mock qui est la cible du test (dans le cas précédent c’est le véhicule l’objet final du test, pas le mannequin qui est néanmoins très important dans le recueil des données). Le mock ne sert qu’à borner précisément un contexte pour permettre une analyse plus facile des objets « réels » à tester.

Pour réaliser un premier mock, je vous propose d’utiliser le framework Moq. Moq est un framework simple mais puissant qui repose intégralement sur les lambdas expressions introduites avec le C# 3 pour son paramétrage. Ceci nous assure une approche « type-safe » et la capacité de pouvoir faire du refactoring de base du code original sans retoucher au code de test.

Un exemple d’utilisation :

public interface IBanque
{
    event EventHandler CasseDuSiecle;
    string Nom { get; set; }
    double ObtenirSoldeCompte(int idCompte);
}

var mock = new Mock<IBanque>();

// lire "dès que la propriété 'Nom' est accédée en lecture,
// - retourner Société Géniale"
// - et effectuer le code de callback"
//   (on aurait pu indiquer une méthode au lieu d'une lambda)
mock.SetupGet(b => b.Nom)
    .Returns("Société Géniale")
    .Callback(
         () => Console.WriteLine("callback du mock")
    );

// lire "dès que la fonction 'ObtenirSoldeCompte' est appellée,
// - pour n'importe qu'elle valeur de id, retourne 15000
//   (on aurait pu indiquer une valeur fixe, un regex ou un range)
// - et lance l'évènement CasseDuSiecle"
mock.Setup(b => b.ObtenirSoldeCompte(It.IsAny<int>()))
          .Returns(15000.0)
          .Raises(b => b.CasseDuSiecle += null, null, EventArgs.Empty);
 
// On obtient notre instance 'factice' de IBanque
IBanque banque = mock.Object;

// Imaginons l'utilisation du mock par des objets réels:
banque.CasseDuSiecle +=
    new EventHandler(
       (s,a) => Console.WriteLine("C'est le casse du siècle!") 
    );
Console.WriteLine(banque.Nom);
Console.WriteLine(banque.ObtenirSoldeCompte(45654));
 
// Retour à notre contexte de test, vérifions que l'appel à 
// 'Nom' a bien été effectué exactement une fois
mock.VerifyGet(b => b.Nom, Times.Once());

Résultat:

callback du mock
Société Géniale
C'est le casse du siècle!
15000

Techniquement il est possible de mocker des interfaces, des classes abstraites ou concrètes pour peu que les méthodes soient virtuelles. Il est également possible de faire du mock récursif, par exemple en mockant une propriété d’une propriété de l’objet principal, directement dans la lambda expression de plus haut niveau.

L'avantage, c'est que pour les habitués des lambdas expressions, l’apprentissage de Moq est très simple!

Un commentaire