unit-testing - unitaire - unit tests




Quelle est la différence entre truquer, se moquer, et talonner? (4)

Je sais comment j'utilise ces termes, mais je me demande s'il y a des définitions acceptées pour truquer , se moquer et faire des acrobaties pour les tests unitaires? Comment les définissez-vous pour vos tests? Décrivez les situations dans lesquelles vous pourriez utiliser chacune.

Voici comment je les utilise:

Fake : classe qui implémente une interface mais contient des données fixes et aucune logique. Renvoie simplement les données "bonnes" ou "mauvaises" en fonction de l'implémentation.

Mock : une classe qui implémente une interface et permet de définir dynamiquement les valeurs à retourner / exceptions à lancer à partir de méthodes particulières et permet de vérifier si des méthodes particulières ont été appelées / non appelées.

Stub : Comme une classe fictive, sauf qu'elle ne permet pas de vérifier que les méthodes ont été appelées / non appelées.

Les mock et les stubs peuvent être générés à la main ou générés par un cadre de simulation. Les fausses classes sont générées à la main. J'utilise principalement des simulacres pour vérifier les interactions entre ma classe et les classes dépendantes. J'utilise des talons une fois que j'ai vérifié les interactions et que je teste d'autres chemins à travers mon code. J'utilise principalement des fausses classes pour extraire des dépendances de données ou lorsque des mock / stubs sont trop fastidieux pour être mis en place à chaque fois.


Il s'agit de rendre les tests expressifs. Je définis des attentes sur un Mock si je veux que le test décrive une relation entre deux objets. Je retourne des valeurs si je mets en place un objet de support pour m'apporter le comportement intéressant dans le test.


Je suis surpris que cette question existe depuis si longtemps et personne n'a encore fourni une réponse basée sur "L'Art des tests unitaires" de Roy Osherove .

Dans "3.1 Introduction des talons" définit un talon comme:

Un stub est un remplacement contrôlable pour une dépendance existante (ou collaborateur) dans le système. En utilisant un stub, vous pouvez tester votre code sans traiter directement la dépendance.

Et définit la différence entre les talons et les faux comme:

La principale chose à retenir à propos des simulacres par rapport aux talons est que les simulacres sont juste comme des talons, mais vous vous opposez à l'objet simulacre, alors que vous n'affirmez pas contre un talon.

Faux est juste le nom utilisé pour les deux stubs et les faux-semblants. Par exemple quand vous ne vous souciez pas de la distinction entre les talons et les faux-semblants.

La façon dont Osherove fait la distinction entre les stubs et les mock, signifie que toute classe utilisée comme un faux pour tester peut être à la fois un talon ou un faux. Ce qui est pour un test spécifique dépend entièrement de la façon dont vous écrivez les contrôles dans votre test.

  • Lorsque votre test vérifie les valeurs dans la classe testée, ou en fait n'importe où sauf le faux, le faux a été utilisé comme un talon. Il fournissait simplement des valeurs à utiliser pour la classe à tester, soit directement à travers les valeurs renvoyées par les appels, soit indirectement en provoquant des effets secondaires (dans certains états) à la suite d'appels.
  • Lorsque votre test vérifie les valeurs du faux, il a été utilisé comme un faux.

Exemple de test où la classe FakeX est utilisée comme stub:

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, cut.SomeProperty);

La fake instance est utilisée comme un talon parce que l' Assert n'utilise pas de fake du tout.

Exemple de test où la classe de test X est utilisée comme simulation:

const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);

cut.SquareIt;

Assert.AreEqual(25, fake.SomeProperty);

Dans ce cas, l' Assert vérifie une valeur sur fake , ce qui fausse un faux.

Maintenant, bien sûr, ces exemples sont très artificiels, mais je vois un grand mérite dans cette distinction. Cela vous permet de savoir comment vous testez vos données et où sont les dépendances de votre test.

Je suis d'accord avec Osherove

Du point de vue de la maintenabilité pure, dans mes tests utilisant des mock crée plus de problèmes que de ne pas les utiliser. Cela a été mon expérience, mais j'apprends toujours quelque chose de nouveau.

Affirmer contre le faux est quelque chose que vous voulez vraiment éviter car cela rend vos tests très dépendants de l'implémentation d'une classe qui n'est pas du tout testée. Ce qui signifie que les tests de la classe ActualClassUnderTest peuvent commencer à se rompre car l'implémentation de ClassUsedAsMock modifiée. Et ça me donne une odeur nauséabonde. Les tests pour ActualClassUnderTest ne doivent de préférence se casser que lorsque ActualClassUnderTest est modifié.

Je me rends compte que l'écriture affirme contre le faux est une pratique courante, particulièrement quand vous êtes un type d'abonné de TDD de type mockist. Je suppose que je suis fermement avec Martin Fowler dans le camp classiciste (voir "Mocks are not Stubs" de Martin Fowler ) et comme Osherove éviter les tests d'interaction (ce qui ne peut être fait qu'en affirmant contre le faux) autant que possible.

Pour le plaisir de lire pourquoi vous devriez éviter les simulacres comme défini ici, google pour "fowler mockist classicist". Vous trouverez une pléthore d'opinions.


Pour illustrer l'utilisation des stubs et des mock, je voudrais également inclure un exemple basé sur « The Art of Unit Testing » de Roy Osherove.

Imaginez, nous avons une application LogAnalyzer qui a la seule fonctionnalité d'impression des journaux. Il n'a pas seulement besoin de parler à un service Web, mais si le service Web génère une erreur, LogAnalyzer doit enregistrer l'erreur dans une autre dépendance externe, en l'envoyant par courrier électronique à l'administrateur du service Web.

Voici la logique que nous aimerions tester dans LogAnalyzer:

if(fileName.Length<8)
{
 try
  {
    service.LogError("Filename too short:" + fileName);
  }
 catch (Exception e)
  {
    email.SendEmail("a","subject",e.Message);
  }
}

Comment testez-vous que LogAnalyzer appelle correctement le service de messagerie lorsque le service Web lève une exception? Voici les questions auxquelles nous sommes confrontés:

  • Comment pouvons-nous remplacer le service Web?

  • Comment pouvons-nous simuler une exception du service Web afin que nous puissions tester l'appel au service de messagerie?

  • Comment saurons-nous que le service de courriel a été appelé correctement ou pas du tout?

Nous pouvons traiter les deux premières questions en utilisant un bout pour le service Web . Pour résoudre le troisième problème, nous pouvons utiliser un objet simulé pour le service de messagerie .

Un faux est un terme générique qui peut être utilisé pour décrire un talon ou un faux. Dans notre test, nous aurons deux faux. L'un sera le service de courrier électronique, que nous allons utiliser pour vérifier que les paramètres corrects ont été envoyés au service de messagerie. L'autre sera un stub que nous utiliserons pour simuler une exception provenant du service web. C'est un bout parce que nous n'utiliserons pas le faux service web pour vérifier le résultat du test, seulement pour nous assurer que le test fonctionne correctement. Le service de courrier électronique est un faux parce que nous allons affirmer qu'il a été appelé correctement.

[TestFixture]
public class LogAnalyzer2Tests
{
[Test]
 public void Analyze_WebServiceThrows_SendsEmail()
 {
   StubService stubService = new StubService();
   stubService.ToThrow= new Exception("fake exception");
   MockEmailService mockEmail = new MockEmailService();

   LogAnalyzer2 log = new LogAnalyzer2();
   log.Service = stubService
   log.Email=mockEmail;
   string tooShortFileName="abc.ext";
   log.Analyze(tooShortFileName);

   Assert.AreEqual("a",mockEmail.To); //MOCKING USED
   Assert.AreEqual("fake exception",mockEmail.Body); //MOCKING USED
   Assert.AreEqual("subject",mockEmail.Subject);

 }
}

Vous pouvez obtenir des informations:

De Martin Fowler à propos de Mock and Stub

Les faux objets ont effectivement des implémentations de travail, mais prennent généralement un raccourci qui les rend impropres à la production

Les talons fournissent des réponses prédéfinies aux appels effectués pendant le test, ne répondant généralement pas à quoi que ce soit en dehors de ce qui est programmé pour le test. Les talons peuvent également enregistrer des informations sur les appels, comme un talon de passerelle de messagerie qui se souvient des messages qu'il a «envoyés», ou peut-être seulement le nombre de messages qu'il a «envoyés».

Les mocks sont ce dont nous parlons ici: des objets pré-programmés avec des attentes qui forment une spécification des appels qu'ils sont censés recevoir.

De xunitpattern :

Faux : Nous acquérons ou construisons une implémentation très légère de la même fonctionnalité que celle fournie par un composant dont dépend le SUT et nous demandons au SUT de l'utiliser à la place du réel.

Stub : Cette implémentation est configurée pour répondre aux appels du SUT avec les valeurs (ou exceptions) qui exerceront le code non testé (voir Bugs de production à la page X) dans le SUT. Une indication clé pour l'utilisation d'un talon de test est d'avoir un code non testé causé par l'incapacité de contrôler les entrées indirectes du SUT.

Mock Object qui implémente la même interface qu'un objet sur lequel dépend le SUT (System Under Test). Nous pouvons utiliser un objet Mock comme point d'observation lorsque nous devons effectuer une vérification de comportement pour éviter d'avoir une exigence non testée (voir Bugs de production à la page X) causée par l'incapacité d'observer les effets secondaires des méthodes invoquées sur le SUT.

Personnellement

J'essaie de simplifier en utilisant: Mock and Stub. J'utilise Mock quand c'est un objet qui renvoie une valeur qui est définie sur la classe testée. J'utilise Stub pour imiter une classe Interface ou Abstract à tester. En fait, peu importe ce que vous appelez, ce sont toutes les classes qui ne sont pas utilisées en production et qui sont utilisées comme classes d'utilité pour les tests.





stub