Dans ce post, j’aimerais vous présenter diverses méthodes permettant d’effectuer des tests de comportements sur vos composants. On ne teste pas le contexte, les données retournées mais bien le comportement de la routine avec l’appelant et les dépendances.
Contexte
On souhaite tester une méthode d’un dépôt(notion de repository en DDD). La routine en question est un service permettant d’envoyer des entités (Demande) à un service Web pour centraliser ces demandes. Les différentes actions effectuées sont les suivantes :
L’appelant appelle la méthode EnvoyerTout prenant en paramètre une liste de Demande.
Le dépôt reçoit la liste et vérifie les informations de synchronisation dans une base de données locale.
Le dépôt envoie la liste des demandes et les informations de synchronisation au service.
Le service répond et fournit des informations permettant d’enregistrer en local que les demandes ont déjà été envoyé au serveur.
Afin de limiter le trafic réseau, on souhaite vérifier que les demandes réellement envoyées au serveur n’avait jamais été envoyées. Cette information est disponible dans la base de données de locale.
On souhaite isoler le comportement de cette routine car nous détectons rapidement les différentes dépendances :
Un service Web qui doit répondre aux différentes requêtes du proxy.
Une base de données contenant des informations de synchronisation.
Une utilisation d’une dépôt permettant de récupérer les informations de l’utilisateur.
Explication du test
Voici donc le test en question :
1: [TestMethod]2: public void EnvoyerToutNAjouteQueLesDemandesJamaisSynchronisees()
3: { 4: utilisateurDepot = mockRepository.Stub<IUtilisateurDepot>(); 5: demandeDeControleService = mockRepository.StrictMock<DemandeDeControleService>(); 6: demandeDeControleServeurDepot = mockRepository.Stub<DemandeDeControleServeurDepot>(demandeDeControleService, utilisateurDepot); 7: Expect.Call(demandeDeControleServeurDepot.GetReplicaId()).Return(Guid.NewGuid());8: demandeDeControleServeurDepot.MettreAJourTousLesWatermark(null, 0);
9: LastCall.IgnoreArguments(); 10: 11: Expect.Call(demandeDeControleService.EnvoyerTout(null)).IgnoreArguments().Return(0).Repeat.Once();
12: mockRepository.ReplayAll(); 13: 14: demandeDeControleServeurDepot.EnvoyerTout(CreerUneListeDeDemandes(5, 2)); 15: 16: ServiceDomain.DemandeDeControle[] demandeDeControleEnvoyees =17: (ServiceDomain.DemandeDeControle[])demandeDeControleService.GetArgumentsForCallsMadeOn(x => x.EnvoyerTout(null)).First().First();
18: Assert.AreEqual(2, demandeDeControleEnvoyees.Length); 19: } Pour décrire ce test commençons par le commencement d’un test à savoir son assertion :
Ligne 18 : Assert.AreEqual(2, demandeDeControleEnvoyees.Length); On vérifie ici que le nombre de demandes envoyés au serveur est bien égale à 2.
On pourrait donc traduire cette assertion de la sorte : Si l’application fournit une liste de 5 demandes au dépôt mais que seuls 2 demandes de cette liste doivent être envoyées au serveur, vérifie que le dépôt n’a envoyé que ces 2 éléments au service.
Pourquoi?
Ligne 14 : demandeDeControleServeurDepot.EnvoyerTout(CreerUneListeDeDemandes(5, 2)); C’est la routine que nous souhaitons tester. Dans le test, le paramètre (la liste de demandes suceptibles d’être envoyées) est fourni par une méthode du test CreerUneListeDeDemandes(5, 2) prenant en paramètre le nombre total de demande à créer et le nombre de demandes à synchroniser (et qui doivent donc réellement être envoyées au serveur).
Et la rapport avec l’assertion?
Ligne 16 et 17 : On récupère le premier paramètre du premier appel (il n’y en a qu’un) de l’exécution de la méthode EnvoyerTout d’un mock (demandeDeControleService) .C’est le cœur même du test et l’objet de l’assertion. Ces paramètres proviennet directement du mock, le service n’est jamais instancié.
Et comment c’est possible?
De la ligne 4 à la ligne 9 : on prépare l’exécution du test en isolant, à l’aide de diverses techniques et fonctionnalités de RhinoMock, les dépendances détectées.
Ligne 4 : on crée un stub du dépot utilisateur, aucun comportement ou données ne nous sont nécessaires. Nous avons juste besoin d’une instance de ce dépot pour l’instanciation ligne 6. Le fait de le “stuber” permet de ne pas exécuter le code de ce dépôt.
Ligne 5 : On crée un mock strict. Toutes les fonctionnalités du service seront recrées et tous les appels vers le service seront redirigés vers ce mock.
Ligne 6 à 12 : Création du mock partiel du composant testé. Toutes les routines réclamant un contexte (ligne 7 et 8, la récupération des informations de synchronisation nécessite une base de données locale) sont redirigés vers le mock. On peut configurer le résultat de l’exécution de la méthode du mock (ligne6 : demandeDeControleServeurDepot.GetReplicaId()).Return(Guid.NewGuid()) ) ou l’on peut déclarer d’ignorer l’appel et les paramètres (ligne 7).
Sur ce mock partiel, on appelle la méthode testée (ligne 11).
Tous les comportement d’isolation ne seront disponibles qu’après la ligne 12. Celle-ci permet d’informer RhinoMock des différentes instructions d’isolation.
Conclusion
Pas de base de données à construire et de données à générer. Pas de service sur un serveur de test. Mais le comportement attendu est vérifié et le sera même en cas de changement de source de données, de modification du contrat de service…
Cool non?



