[BuildStuff 2015] Good unit testing

A l’occasion de l’événement BuildStuff 2015 en Lituanie, à Vilnius plus précisément, j’ai eu l’occasion d’assister à la conférence de Kevlin Henney ayant pour thème « Programming with GUT(s) ».

Good Unit Testing (GUT)

Réussir à faire un assert avec l’outil de votre choix n’est que le début, et c’est bien là que ce situe la tâche la moins complexe.
Il ne s’agit pas plus d’avoir 100% de couverture de code ou de faire du Test Driven Development pour que les tests soient efficaces.

Durant ce billet, nous allons parcourir plusieurs petites étapes afin de rédiger de bons tests unitaires.

Lire la suite

Le chemin vers la qualité est-il unique ? (Part 2 : la Production)

le chemin

Nous avons vu précédemment qu’il n’existait pas de méthodologie parfaite (agile ou non). Que nos besoins en tant qu’unité (équipe, société ou individu) allaient dicter les méthodologies que nous voulons ou pouvons suivre.

A présent, que se passe-t-il lorsque nous produisons ? Comment organiser notre travail effectif afin d’atteindre un niveau de qualité le plus élevé possible ?

Là où les méthodologies peuvent parfois s’arrêter, certaines nous donnent des clés pour améliorer notre code en plus de notre façon de travailler. Et comme le nombre de méthodologies, il est aisé de crouler sous le nombre de « bonnes pratiques » et finalement ne pas savoir par quoi commencer. Voire de s’y perdre et de ne rien faire du tout.

Ici aussi, y a-t-il une bonne méthode à appliquer ?

En plus de notre expérience, deux conférences de Paris Web peuvent nous aiguiller dans cette réflexion.

Tout d’abord, « Code de qualité : ce qu’il faut savoir«  par Julien Wajsberg et Anthony Ricaud et « 100 % de revue de code«  par Agnès Haasser.

 

Quels outils sont utilisés, pourquoi et comment sont-ils intégrés ?

La revue de code est le point commun entre ces deux conférences. Et à raison : cette pratique est initialement la plus […] simple à mettre en place (une chaise suffit !).

La manière classique consiste à faire venir un pair à côté de soi et relire ensemble le code produit à la recherche d’erreurs ou d’oublis techniques comme fonctionnels. Ensuite, de partager sur les améliorations possibles.

Son principal inconvénient reste son coût en temps effectif, tout comme les interruptions que le demandeur réalise auprès de ses collègues.

Chez Soft’it, nous avons pris la décision de ne pas nécessairement impliquer le demandeur dans la revue de code. Le temps est provisionné et c’est à chacun de vérifier ce que les autres ont envoyé. Si besoin, le relecteur fera une remarque directement ou validera simplement le code produit.

Du côté d’Agnès Haasser, les « pull requests » de Git se sont avérés être la meilleure réponse aux besoins de son équipe. Le principe est le même : soumettre son code à la validation des pairs de manière asynchrone. Ce qu’ils ont rajouté est une surcouche de sécurité : préserver la branche master de toute erreur éventuelle. De plus, tous les devs peuvent participer et/ou suivre cette relecture de code, réduisant drastiquement le bus factor.

Une autre pratique pouvant remplacer la revue de code est le pair programming. On place ici aussi 2 développeurs face à une même machine. La revue de code a lieu en même temps que le développement.

Seulement, ce type de mise en place est coûteuse en temps si les développements ne sont faits que de cette manière. Notre choix interne a été de ne l’utiliser que dans trois cas très précis : l’arrivée de nouvelles personnes dans l’équipe, la découverte de nouvelles technologies ou langages et enfin le développement de parties complexes et/ou critiques. Dans le premier cas, le nouvel arrivant participe à la production tout en apprenant. Et nous avons un filet de sécurité en cas de soucis. Ce filet existera également dans le deuxième cas, tout en apportant un recul aux développeurs qui s’émuleront mutuellement. Dans le dernier, l’intérêt est assez évident : deux paires d’yeux valent mieux qu’une. Et si la fonctionnalité est trop complexe, revenir sur le code de quelqu’un peut s’avérer compliqué et finalement faire perdre plus de temps que d’en gagner.

Même si de manière générale le pair programming implique qu’un des développeurs s’occupe du code de production et l’autre des tests, on peut coupler tests et vérification.

 

Le testing, justement. S’il est considéré comme obligatoire chez certains (c’est notre cas chez Soft’it), il ne l’est pas nécessairement d’après messieurs Wajsberg et Ricaud. Ou du moins dès le début.

La décision prise chez Mozilla est de ne pas rendre obligatoire les tests à la création d’une classe. Mais une fois que la première brique est posée, il ne doit plus y avoir de développement sans test.

Pourquoi ce choix ? Parce qu’il est parfois compliqué de définir un premier test concluant lorsque l’on commence à développer une nouvelle fonctionnalité.

Travailler en TDD rendrait évidemment cette règle absurde. Mais cette pratique n’est pas si évidente à mettre en place : certaines architectures, utilisant des ORMs par exemple, rendent l’utilisation de doublures de tests trop lourdes pour être réellement efficaces. On se tournera alors vers des tests d’intégration (qui arrivent plus tard dans le cycle de développement) afin de valider les cas d’utilisation.

Alors quand commencer à tester ?

Lorsque votre architecture le permet. Et elle le pourra toujours très tôt. Lorsqu’il est intéressant de tester également (le faire sur les getters et setters n’a pas d’intérêt en soi). Plus les tests commencent tard, plus il est compliqué de commencer (quel sera le premier test ? Dois-je revenir sur ce qui n’a pas encore été testé ?). 

Leur présence offre un filet de sécurité à l’équipe. Techniquement d’abord : en cas de besoin de refactoring ou de reprise de code par un autre développeur. Ils sécurisent la modification : si on casse quelque chose, on le sait très tôt.
Fonctionnellement, écrire des tests correspondant au cheminement d’un user donnera une meilleure confiance dans le produit terminé. Notamment lors de corrections de bugs. Idéalement, un ou plusieurs tests devront valider la correction, scelleront sa correction et serviront de gardiens face aux régressions.

 

Enfin, l’intégration continue. Le nom du produit varie, mais l’intérêt et l’implémentation restent les mêmes : s’assurer qu’à tout moment, on ne livre pas un produit cassé aux clients.
La solution est modulable : vous pouvez limiter son utilisation à une machine de build, les développeurs sont alors assurés que les fichiers disponibles sur le repository ne sont pas à même de les empêcher de travailler. Exécuter les tests automatiques (unitaires et d’intégration), voire utiliser un analyseur de code (FxCop, NDepend, …) afin de sécuriser les règles de développement. 

Si les pratiques se retrouvent souvent dans les organisations, leurs applications diffèrent parfois très fortement. L’intention est la même : sécuriser les développements et les développeurs. Mais comme dans toute équipe, les besoins et contraintes ne sont jamais les mêmes. Et suivant ces contraintes, chacun doit plier plus ou moins chaque principe ou philosophie. Il se peut que vous arriviez dans une équipe et que certaines de leurs pratiques, notions ou terminologies vous semblent étranges, voire contre-productives. Mais gardez en tête qu’ils ont peut-être des contraintes très fortes qui ne leur permettent pas d’appliquer à la lettre les bonnes pratiques.

Comprenez vos besoins et ceux de l’équipe avant d’amener une réponse toute faite. Prenez en compte leurs peurs, limites et attentes et apportez-leur les pratiques qui leur conviennent.
C’est là le fondement même de l’agilité, utilisez des canevas de solutions plutôt que de vous baser sur celles que vous avez pu connaître avant.

[Témoignage] Voluntis : Comment passer du WebForms au MVC sans risque : ASP.NET MVC & Testing

Suite à notre récente formation sur le passage de l’ASP.NET WebForms à l’ASP.NET MVC en exploitant le testing, notre client, Erwan de Cadoudal (Team leader chez Voluntis [éditeur de logiciels dans le domaine médical], et demandeur de cette formation), a eu l’amabilité de répondre à nos questions.

Soft’it – Quelles sont vos motivations à l’origine de ce besoin de formation MVC et Testing MVC ?

E. de Cadoudal : « Voluntis met en œuvre des applications médicales dont le code peut présenter un risque pour les patients. Par exemple nos solutions mettent en œuvre des algorithmes médicaux complexes qui permettent d’aider le patient dans sa décision thérapeutique.

Les solutions de Voluntis sont au carrefour du numérique et du thérapeutique. Dans ce contexte, MVC nous parait une solution moderne et efficace pour des déploiements dans de multiples contextes d’usage, depuis le smartphone du patient jusqu’à l’écran du médecin. Par ailleurs nous sommes convaincus que l’approche MVC et les tests unitaires sont des solutions aujourd’hui très efficaces et très pertinentes pour développer rapidement des solutions industrialisées dans le contexte très réglementé des dispositifs médicaux. »

Soft’it – De quelle manière Soft’it y a répondu ? Qu’est-ce qui vous a plu dans la réponse de Soft’it à votre besoin ?

E. de Cadoudal : « L’approche de Soft’it nous a convaincu car le plan proposé a été était fait sur mesure avec une prise en compte de nos besoins et de nos équipes. La décision de suivre la formation dans nos locaux pour un nombre significatif de développeurs a été déterminante.
De plus certains exemples illustrant les exercices étaient proches de notre métier et nous parlaient bien. Par exemple pour nous il est plus clair de parler de liste de patients que de liste de bons de commande. Bien que cette session nous a paru un peu trop concentrée, chacun d’entre nous est reparti avec bases communes et des exemples concrets : du code et des présentations.

Nous avons également pu apprécier le dynamisme, l’expertise et le professionnalisme des équipes de formation, qui ont permis de conserver un bon rythme et de faciliter des échanges constructifs, et ont été des facteurs clés du succès des formations que nous avons suivies.« 

Soft’it – Quels sont les résultats et/ou quelle dynamique cette approche a-t-elle amené ?

E. de Cadoudal : « L’intégration des patterns « MVC », initiés par nos équipes de R&D en 2014, devient le modèle de référence pour les nouveaux projets de Voluntis. La formation nous a permis de mieux appréhender cette technologie et ses impacts sur notre méthodologie de travail.

En parallèle, nous travaillons en étroite collaboration avec nos équipes de tests et validation pour augmenter la couverture de notre code par du test unitaire. Nous anticipons que l’automatisation de ceux-ci, couplée à nos méthodologies Agile, nous permettra de réduire significativement notre investissement de tests tout en améliorant la qualité de nos produits. »

Soft’it – Nous recommanderiez-vous ? Comptez-vous refaire appel à nous ?

E. de Cadoudal : « Seulement une partie des développeurs a suivi la formation, nous comptons remonter une session identique à la précédente.
L’approche de Soft’it a été la bonne dans notre contexte et je recommande la formule pour des équipes techniques qui travaillent sur un framework partagé avec les développeurs. »

 

Un grand merci à Erwan pour ces réponses et ce feedback positif. L’équipe ayant suivie la formation a par ailleurs donné un 4/4 au ROTI pour Marien et Rémi !

L’équipe Soft’it se plie donc en quatre pour vous fournir les meilleures formations adaptées à vos besoins.
Contactez-nous si vous souhaitez avoir plus de renseignements.

[Formation] Comment passer du WebForms au MVC sans risque : ASP.NET MVC & Testing

L’ASP.NET MVC est la technologie web .NET depuis plusieurs années maintenant. Cette plateforme nous permet de mieux maîtriser le rendu HTML, le découplage métier/interface et surtout le testing.

Voici donc 2 sujets bien connus de l’équipe et qui tiennent à cœur à Soft’it : les applications web (ASP.NET MVC) et la qualité (testing).

Un de nos clients, Voluntis (éditeur de logiciels dans le domaine médical), a eu une problématique que grand nombre de sociétés rencontrent actuellement : comment passer de l’ASP.NET WebForms à l’ASP.NET MVC sans régression, et sans impacter les utilisateurs.

Même si une application WebForms fonctionne correctement depuis des années, il y a un moment où la dette technique devient un frein aux améliorations et vous coûte plus cher que les évolutions ne peuvent vous apporter.
Nous l’avons constaté avec tous nos clients, il est nécessaire de toujours prendre le temps de rester « aux goûts du jour » afin d’être en mesure de toujours proposer des nouveautés, et surtout de ne pas décourager vos développeurs en travaillant sur des technos « moins sexy ».

L’un des gros avantages du MVC est la facilité à appliquer du test sur la plupart des couches de votre application (Modèle, Contrôleur, voire Vue). Pourquoi ne pas utiliser cet avantage précis pour migrer sans risque ?!

formation-mvc-voluntisC’est sur cette base que nous avons construit une formation sur-mesure pour 10 développeurs/architectes de chez Voluntis :

  • jour 1 : introduction à l’ASP.NET MVC et aux bonnes pratiques, puis exemples de passage du WebForms au MVC
  • jour 2 : introduction à la notion de testing et comment tester une application MVC

La pratique étant le meilleur moyen d’apprendre, chaque jour fut composé en moyenne de 40% d’atelier !

Pour remplir cette mission, nous avons envoyé 2 membres de l’équipe Soft’it :

Marien Monnier
Rémi Lesieur-Bridel

Le résultat : une formation sur-mesure calée aux besoins de Voluntis, un travail exemplaire de Marien et Rémi, une équipe très satisfaite (4/4 au ROTI pour tout le monde) et un client ravi :

De plus, Erwan de Cadoudal (Team leader chez Voluntis, et demandeur de cette formation), nous a fait un témoignage très positif.

Si vous souhaitez faire une formation technique et/ou qualité sur-mesure, contactez-nous.

[Formation] Testing et TDD en Java…

Un des membres de l’équipe Soft’it – Rémi Lesieur pour ne pas le nommer – a réalisé lundi et mardi dernier une formation Testing/TDD à destination d’une équipe expérimentée en Java, pour une division de l’Armée française.

L’objectif de la formation était double: présenter ce qu’est le testing et la TDD (philosophie et théorie), tout en démontrant l’intérêt ainsi que le gain de temps et de la qualité.
Eh oui, c’est souvent difficile en tant que développeur de se dire qu’écrire du code pour tester du code, ça revient à gagner du temps! C’est pourtant bien le cas.

Rémi a donc abordé la logique du testing d’une manière générale en démontrant que ce n’est pas parce qu’on teste à la main, que notre code est bon et surtout pérenne (régressions non visibles).
Puis il a présenté les différents types de tests (unitaires/intégration), ainsi que la notion de mocking (voir « Dummy, Fake, Stub, Mock et Spy, les tests unitaires avec l’aide de Moq.« ).

Pour finalement attaquer le testing dans l’eXtreme Programming (XP) : le Test Driven Development (TDD).

Au total, 2 jours pour 10 développeurs/lead-techs mélangeant théorie et beaucoup de pratiques sous forme de Katas, en pair programming.

Un bon succès puisque 7 personnes sur 10 ont donné la note de 5/5, et les 3 restantes, 4/5. La formation construite sur mesure par Rémi a donc visé juste.

Si vous êtes intéressé par des formations et/ou du coaching sur du testing (et tout autre outil/méthodologie autour de la qualité), dites-le nous!

Dummy, Fake, Stub, Mock et Spy, les tests unitaires avec l’aide de Moq.

J’ai travaillé dans plusieurs sociétés et encore plus d’équipes différentes. Souvent, on m’a parlé de tests unitaires, que c’était important pour la stabilité et la robustesse de la solution.
Je me suis rendu compte qu’il s’agissait d’un abus de langage : la plupart du temps, j’étais devant des tests d’intégration. Cette erreur avait très souvent la même explication : les équipes n’avaient pas correctement isolé leur code.

L’isoler veut dire permettre à son applicatif de s’affranchir de ses dépendances externes (sa base de données par exemple).
Je vais ici tenter d’expliquer les différentes techniques permettant de couper ou simuler ces dépendances.

Les personnes qui pourraient être intéressées par cet article sont principalement (mais pas seulement) : 

  • Les développeurs débutants dans les tests unitaires.
  • Ceux ayant déjà survolé le sujet voulant approfondir leurs connaissance et compréhension.

Je risque pour les second d’enfoncer quelques portes ouvertes. Mais il est toujours bon de rappeler les bases.

Que sont les doublures de test ?

Durant le développement de mon application, j’aurai sûrement à implémenter à un moment donné des tests unitaires.
Chaque test doit être atomique et ne tester que son état. Pas celui d’autres méthodes ou objets. Je ne dois, par exemple, jamais atteindre la base de données ou le système de fichier. Je passerai alors dans l’univers des tests d’intégration.
Du coup, je dois faire croire à mon code soit que j’appelle bien la méthode qu’il m’a demandé d’utiliser soit que l’objet dont il peut avoir besoin existe.
Cet ensemble de subterfuges s’appellent des doublures de test. Ceux-ci sont identifiés et nommés comme suit : Dummy, Fake, Stub, Mock, Spy.
Leurs implémentations peuvent aller du trivial au casse-tête si je n’utilise pas d’outil spécifique existant.
C’est pourquoi j’utiliserai la bibliothèque Moq pour illustrer mes exemples.
A noter que si en général, l’utilité des doublures de test est avéré pendant les tests unitaires; je peux dans certains cas précis utiliser leur principe ailleurs dans mon projet.

Définitions et implémentations

Dummy

Il s’agit de faire croire à ma destination que l’objet existe. Celui-ci ne fera rien, il ne contiendra rien non plus. Mais il existera aux yeux de mon code testé.
Exemple : Admettons que nous ayons une classe que nous appellerons MasterClass. Son constructeur unique prend en paramètre une implémentation d’une interface lui servant de DAL.
Nous avons un test à faire : qu’un GUID soit bien généré suite à l’appel du constructeur. Ce test n’a pas besoin de mon appel à la DAL.
Alors soit, je peux passer null au constructeur :

[code language= »csharp »]
[TestMethod]
public void MasterClass_Contient_Guid()
{
var master = new MasterClass(null);
Assert.IsNotNull(master.Guid);
}
[/code]

Si d’aventure mon constructeur appelle ma DAL directement, mon test ne passera jamais (le constructeur renverra invariablement une NullReferenceException).
Je peux me servir de Moq afin de faire croire à mon constructeur que si, je lui ai bien donné une instance de l’interface :

[code language= »csharp »]
[TestMethod]
public void MasterClass_Contient_Guid()
{
// Initialisation des variables utilisant Moq
var repo = new Mock<IDataRepository>();

IDataRepository dummyRepo = repo.Object;

// On appelle le constructeur avec le dummy
var master = new MasterClass(dummyRepo);

// Enfin, on effectue une assertion qui n’a pas besoin
// d’utiliser IDataRepository.
Assert.IsNotNull(master.Guid);
}
[/code]

Attention : avec Moq, l’appel à une méthode d’un Dummy renverra toujours null. Gare à vos validations !

Fake

Un Fake est une implémentation simplifiée d’un comportement attendu. Ici, on ne fait pas croire à notre code quelque chose; on l’a implémenté explicitement. Il sera juste simplifié et ne fera appel à aucune dépendance.
Je n’ai pas besoin de Moq pour cette doublure de test. Je dois créer une classe qui me servira uniquement pour mes tests.

[code language= »csharp »]
public class FakeDataAccessLayer : IDataRepository
{
private int nbInstances;
public FakeDataAccessLayer(int nbInstances)
{
this.nbInstances = nbInstances;
}

List<SlaveClass> IDataRepository.GetSlaves()
{
var output = new List<BusinessLayer.SlaveClass>();

for(int i = 0; i < this.nbInstances; i++)
output.Add(new SlaveClass() { Id = i, Name = i.ToString() });

return output;
}

int IDataRepository.Save(MasterClass masterClass)
{
throw new NotImplementedException();
}

int IDataRepository.Save(SlaveClass slaveClass)
{
throw new NotImplementedException();
}
}
[/code]

Bien évidemment, je n’implémente que ce dont j’ai besoin. Si mon Fake ne teste jamais les méthodes de sauvegarde, il est même préférable de laisser un renvoi d’exception. Ça me rappellera que ce n’est pas normal de passer par cette méthode.
J’utiliserai le Fake de cette manière dans les tests unitaires :

[code language= »csharp »]
[TestMethod]
public void MasterClass_NbSlaves_Retourne_Liste_Count()
{
int nbInstances = 3;
var fakeRepo = new FakeDataAccessLayer(nbInstances);

var master = new MasterClass(fakeRepo);

Assert.AreEqual(nbInstances, master.NbSlaves);
}
[/code]

Le Fake peut également être très utile en dehors des tests unitaires :
Vous commencez un nouveau développement. Vous n’avez aucune information concernant le mode de stockage de vos données.
Cela peut être voulu (Clean Architecture) ou pas (on vous lance sur le développement alors que tout le monde n’est pas encore d’accord sur ce point, au moins).
En utilisant un Fake, vous vous affranchissez des accès extérieurs. Vous pouvez commencer à coder sans attendre de décision supplémentaire. Il ne vous restera enfin qu’à changer l’implémentation à passer à votre appli / site.

Stub

Je reprends le principe du Fake, mais je vais être plus fainéant. Il n’y aura aucune implémentation explicite, Moq me sera utile dans ce cas. Je ne présenterai que ce dont j’ai besoin.

[code language= »csharp »]
[TestMethod]
public void MasterClass_Save_Met_A_Jour_Id()
{
// Initialisation des variables utilisant Moq
var repo = new Mock<IDataRepository>();

repo.Setup(m => m.Save(It.IsAny<MasterClass>())).Returns(5);

IDataRepository stubRepo = repo.Object;

// On appelle le constructeur avec le stub
var master = new MasterClass(stubRepo);

master.Save();

Assert.AreEqual(5, master.Id);
}
[/code]

La méthodeSetup(Expression<Action<T>>) de Moq me permet de créer une implémentation de la méthode Save(MasterClass) de IDataRepository.
Dans ce cas précis, je demande à Moq :

« Lorsque tu recevras un appel à la méthode Save(MasterClass). Quelle que soit la valeur du paramètre masterClass (It.IsAny<MasterClass>()), renvoie toujours la valeur 5. »

Rappel : Je ne veux pas tester le retour de la fonction Save(MasterClass) de IDataRepository, je veux m’assurer que la méthode Save(MasterClass) de MasterClass utilise bien le retour de cette première.

Mock

Il est parfois compliqué d’aller vérifier le comportement d’une méthode. Notamment lorsque celle-ci va manipuler d’autres objets ou bien appeler leurs méthodes.

On parle également de « behaviour verification« , expression un peu plus facile à comprendre.

J’utiliserai le Mock le plus souvent pour vérifier une propagation.

Ce qu’il faut garder à l’esprit lorsque l’on parle de Mock, c’est que je vérifie des états et des valeurs que je maîtrise et connais. Si je n’en ai pas le pouvoir (ou si ça n’est pas mon problème), alors je parlerai de Spy.
Admettons qu’au moment de la sauvegarde, si une exception est levée, je dois l’enregistrer dans un fichier quelconque. Celui-ci contiendra la valeur du message de l’exception.

[code language= »csharp »]
[TestMethod]
public void MasterClass_Save_Enregistre_Message_Exception()
{
// Initialisation des variables utilisant Moq
var repo = new Mock<IDataRepository>();
var fileLogger = new Mock<IFileAccess>();

repo.Setup(r => r.Save(It.IsAny<MasterClass>())).Throws(new Exception("TestException"));

var master = new MasterClass(repo.Object);
master.SetFileLogger(fileLogger.Object);

master.Save();

fileLogger.Verify(f => f.LogMessage("TestException"));
}
[/code]

J’utilise encore la méthode Setup(Expression<Action<T>>) de Moq. Ici, je lui demande de me renvoyer invariablement une exception. Exception dont je connais le message, puisque c’est moi qui le passe.
Arrive la fonction Verify(Expression<Action<T>>, Times). Dans ce cas, je demande à Moq de vérifier que j’ai bien appelé la méthode LogMessage(string) avec le paramètre d’entré égal à « TestException ».
Dans ce cas, je peux avoir appelé la méthode LogMessage(string) n fois pendant mon traitement. La seule chose qui m’intéresse, c’est de savoir si au moins une fois, mon paramètre était « TestException ».

Spy

Le Spy est un dérivé du Mock. Cependant, si dans ce dernier, je vérifiais les paramètres, ici, je m’assure uniquement que je suis passé (ou non) par un chemin ou une méthode.
Reprenons le test précédent et tournons le en Spy :

[code language= »csharp »]
[TestMethod]
public void MasterClass_Save_Enregistre_Message_Exception_Spy()
{
// Initialisation des variables utilisant Moq
var repo = new Mock<IDataRepository>();
var fileLogger = new Mock<IFileAccess>();

repo.Setup(r => r.Save(It.IsAny<MasterClass>())).Throws(new Exception());

var master = new MasterClass(repo.Object);
master.SetFileLogger(fileLogger.Object);

master.Save();

fileLogger.Verify(f => f.LogMessage(It.IsAny<string>()), Times.AtLeastOnce());
}
[/code]

Le code du test est quasiment le même.
Les seules différences sont que l’exception ne contient plus de message explicite et que Verify(Expression<Action<T>>, Times) ne s’intéresse plus à la valeur du paramètre passé à LogMessage(string).La raison est que dans le Spy, les valeurs passées ne m’intéressent pas. Soit parce que je ne les maîtrise pas soit parce qu’elles ne sont pas au cœur de ma problématique.
J’ai également rajouté Times.AtLeastOnce(), de manière assez évidente, vous aurez compris que je veux vérifier que j’ai appelé « au moins une fois » la méthode. Je peux bien évidemment demander un nombre fixe de fois.

Quelle est la différence fondamentale avec Mock ? Rien ne m’assure que dans le Spy, j’ai bien appelé LogMessage(string) depuis l’endroit du code que je veux valider.

En plus clair : prenons ma méthode Save(MasterClass). Admettons que j’appelle LogMessage(string) autrepart ;pour logger les valeurs de mes paramètres envoyés à IDataRepository par exemple. Et que, par contre, je n’appelle pas ou plus cette méthode lorsqu’une exception est levée… Alors Spy considèrera mon test unitaire en « Réussite » : j’ai correctement appelé LogMessage(string) au moins une fois. A l’inverse, Mock le marquera en « Échec » car je n’ai pas validé la valeur du paramètre passé.
J’espère vous avoir suffisamment présenté les différences, points communs et subtilités des doublures de test. Le sujet est assez complexe. De plus, je me suis rendu compte pendant cette rédaction que je confondais certains d’entre eux. Si j’ai pu éclairer certains d’entre vous, ce serait une excellente chose.
Sources :
Mocks Aren’t Stubs (Martin Fowler)