web-dev-qa-db-fra.com

Quelle est la différence entre truquer, se moquer et se moquer?

Je sais comment utiliser ces termes, mais je me demande s’il existe des définitions acceptées pour simuler , se moquer et boucher pour les tests unitaires? Comment les définissez-vous pour vos tests? Décrivez des situations dans lesquelles vous pourriez utiliser chacun.

Voici comment je les utilise:

Fake : une 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 de manière dynamique les valeurs à renvoyer/les exceptions à lever à partir de méthodes particulières et permet de vérifier si des méthodes particulières ont été appelées ou non.

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.

Mock et stubs peuvent être générés à la main ou par un framework moqueur. 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 stubs une fois que j'ai vérifié les interactions et que je teste des chemins alternatifs dans mon code. J'utilise principalement de fausses classes pour résumer des dépendances de données ou lorsque les mock/stubs sont trop fastidieux à configurer à chaque fois.

563
tvanfosson

Vous pouvez obtenir des informations:

De Martin Fowler à propos de Mock and Stub

Les objets Fake ont des implémentations fonctionnelles, mais prennent généralement un raccourci qui les rend impropres à la production

Stubs fournit des réponses prédéfinies aux appels passés pendant le test et ne répond généralement pas du tout à des événements autres que ceux programmés pour le test. Les talons peuvent également enregistrer des informations sur les appels, tels qu'un talon de passerelle de messagerie qui se souvient des messages qu'il a "envoyés", ou peut-être uniquement du nombre de messages qu'il "a envoyés".

Mocks sont ce dont nous parlons ici: des objets préprogrammés avec des attentes qui constituent une spécification des appels qu’ils sont censés recevoir.

De xunitpattern :

Fake: 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 demandons au SUT de l'utiliser au lieu 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 Bogues de production à la page X) dans le SUT. Une indication clé pour l’utilisation d’un talon de test est le 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 en tant qu'objet dont dépend le système sous test (SUT). Nous pouvons utiliser un objet factice comme point d'observation lorsque nous devons effectuer une vérification du comportement afin d'éviter d'avoir une exigence non testée (voir Bugs de production à la page X) en raison de l'incapacité à observer les effets secondaires de l'invocation de méthodes sur le SUT.

Personnellement

J'essaie de simplifier en utilisant: Mock et Stub. J'utilise Mock lorsqu'il s'agit d'un objet qui renvoie une valeur définie pour la classe testée. J'utilise Stub pour imiter une classe d'interface ou abstraite à tester. En fait, peu importe comment vous l'appelez, ce sont toutes des classes qui ne sont pas utilisées en production et qui sont utilisées comme classes utilitaires pour les tests.

452
Patrick Desjardins

Stub - un objet qui fournit des réponses prédéfinies aux appels de méthodes. 

Mock - un objet sur lequel vous définissez les attentes.

Fake - un objet avec des capacités limitées (à des fins de test), par ex. un faux service web. 

Test Double est le terme général utilisé pour désigner les moignons, les faux et les faux. Mais de manière informelle, vous entendez souvent les gens les appeler simplement des moqueurs. 

171
Mike

Je suis surpris que cette question existe depuis si longtemps et que personne n’ait pour le moment fourni de réponse basée sur de Roy Osherove dans "L’art du test unitaire" .

Dans "3.1 Introduction aux stubs", définit un stub comme suit:

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 avec la dépendance.

Et définit la différence entre stubs et mock comme suit:

La principale chose à retenir à propos des moignons et des moignons est qu’ils sont comme des moignons, mais vous affirmez contre l’objet simulé, alors que vous n’affirmez pas contre un moignon.

Fake n'est que le nom utilisé pour les moignons et les moqueries. Par exemple, lorsque vous ne vous souciez pas de la distinction entre les talons et les moqueries.

La manière dont Osherove distingue les moignons des mocs signifie que toute classe utilisée comme faux pour le test peut être à la fois un moignon et un moignon. Pour un test spécifique, cela 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 sous test, ou en réalité n'importe où sauf le faux, le faux est utilisé comme talon. Elle fournissait simplement des valeurs à utiliser par la classe testée, soit directement par les valeurs renvoyées par ses appels, soit indirectement par le biais d'effets secondaires (dans certains états) résultant d'appels sur cette classe.
  • Lorsque votre test vérifie les valeurs du faux, il a été utilisé comme un simulacre.

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);

L'instance fake est utilisée en tant que stub car la Assert n'utilise pas du tout fake.

Exemple de test où la classe de test X est utilisée comme modèle:

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

cut.SquareIt;

Assert.AreEqual(25, fake.SomeProperty);

Dans ce cas, la variable Assert vérifie la valeur de fake, ce qui en fait un faux.

Bien entendu, ces exemples sont hautement artificiels, mais cette distinction me semble très utile. Cela vous permet de savoir comment vous testez votre matériel et où se trouvent les dépendances de votre test.

Je suis d'accord avec Osherove

du point de vue de la maintenabilité pure, dans mes tests, l’utilisation de simulacres crée plus de problèmes que de ne pas les utiliser. C’est mon expérience, mais j’apprends toujours quelque chose de nouveau.

Vous voulez vraiment éviter les affirmations contre le faux, car cela rend vos tests très dépendants de la mise en oeuvre d'une classe qui n'est pas du tout celle à tester. Ce qui signifie que les tests de la classe ActualClassUnderTest peuvent commencer à être rompus car l'implémentation de ClassUsedAsMock a été modifiée. Et cela m'envoie une odeur nauséabonde. Les tests pour ActualClassUnderTest devraient de préférence être interrompus uniquement lorsque ActualClassUnderTest est modifié.

Je me rends compte que l’écriture d’affirmations contre le faux est une pratique courante, en particulier lorsque vous êtes un type mockist d’abonné à TDD. J'imagine que je suis fermement aux côtés de Martin Fowler dans le camp des classicistes (voir "Mocks are Stubs" de Martin Fowler ) et, comme Osherove, évite les tests d'interaction (qui ne peuvent être réalisés qu'en affirmant contre le faux) autant que possible .

Pour vous amuser à comprendre pourquoi vous devriez éviter les simulacres tels que définis ici, allez sur Google pour "fowler mockist classicist". Vous trouverez une pléthore d'opinions.

78
Marjan Venema

Pour illustrer l’utilisation des moignons et des mocs, 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 doit non seulement communiquer avec un service Web, mais si ce dernier génère une erreur, LogAnalyzer doit consigner l'erreur dans une dépendance externe différente 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 vérifier que LogAnalyzer appelle correctement le service de messagerie lorsque le service Web génère 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 de pouvoir tester l'appel au service de messagerie?

  • Comment saurons-nous que le service de messagerie a été appelé correctement ou à l'adresse All?

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

Un faux est un terme générique qui peut être utilisé pour décrire un talon ou une maquette. Dans notre test, nous aurons deux faux. L’un sera la maquette du service de messagerie, que nous utiliserons pour vérifier que les paramètres corrects ont été envoyés au service de messagerie. L’autre sera un talon que nous utiliserons pour simuler une exception émise par le service Web. C’est un talon car nous n’utiliserons pas le faux service Web pour vérifier le résultat du test, mais pour nous assurer que le test s’exécute correctement. Le service de messagerie électronique est un simulacre, car nous affirmerons 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);

 }
}
7
nanospeck

Il s’agit de rendre les tests expressifs. Je mets les attentes sur un simulacre si je veux que le test décrive une relation entre deux objets. Je stub renvoyer les valeurs si je configure un objet de support pour me amener au comportement intéressant dans le test.

5
Steve Freeman

Comme indiqué dans la réponse la plus votée, Martin Fowler discute de ces distinctions dans Mocks Is not Stubs , et en particulier dans la sous-rubrique La différence entre Mocks et Stubs , veillez donc à lire cet article.

Plutôt que de se concentrer sur comment ces choses sont différentes, je pense qu'il est plus instructif de se concentrer sur pourquoi il s'agit de concepts distincts. Chacun existe dans un but différent.

Faux

Un fake est une implémentation qui se comporte "naturellement", mais n'est pas "réelle". Ce sont des concepts flous et ainsi différentes personnes ont des conceptions différentes de ce qui rend les choses factices.

Un exemple de faux est une base de données en mémoire (par exemple, en utilisant sqlite avec le magasin :memory:). Vous ne l'utiliserez jamais pour la production (car les données ne sont pas conservées), mais c'est parfaitement adéquat en tant que base de données à utiliser dans un environnement de test. C'est aussi beaucoup plus léger qu'une "vraie" base de données.

Comme autre exemple, vous utilisez peut-être une sorte de magasin d’objets (par exemple, Amazon S3) en production, mais lors d’un test, vous pouvez simplement enregistrer des objets dans des fichiers sur le disque; alors, votre implémentation "enregistrer sur le disque" serait un faux. (Ou vous pouvez même simuler l'opération "enregistrer sur le disque" en utilisant plutôt un système de fichiers en mémoire.)

Troisième exemple, imaginez un objet fournissant une API de cache. un objet qui implémente l'interface correcte mais qui n'effectue simplement aucune mise en cache mais renvoie toujours un cache manquant serait une sorte de faux.

Le but d’un faux est pas d’affecter le comportement du système testé , mais plutôt de simplifier la mise en oeuvre du test (en supprimant les poids inutiles ou lourds dépendances).

Stubs

A stub est une implémentation qui se comporte "de façon non naturelle". Il est préconfiguré (généralement par la configuration de test) pour répondre à des entrées spécifiques avec des sorties spécifiques.

Le but d'un stub est de mettre votre système à l'essai dans un état spécifique. Par exemple, si vous écrivez un test pour du code qui interagit avec une API REST, vous pouvez tronquer l'API REST avec une API. qui renvoie toujours une réponse prédéfinie ou qui répond à une demande d'API avec une erreur spécifique. De cette façon, vous pourriez écrire des tests qui font des affirmations sur la façon dont le système réagit à ces états; Par exemple, testez la réponse de vos utilisateurs si l'API renvoie une erreur 404.

Un talon est généralement implémenté pour répondre uniquement aux interactions exactes auxquelles vous lui avez demandé de répondre. Mais la fonctionnalité clé qui fait de quelque chose un talon est son but: un talon consiste à configurer votre scénario de test.

Se moque

Un mock est similaire à un stub, mais avec vérification ajouté dans. Le but de la simulation est de faire des assertions sur la manière dont votre système testé a interagi avec la dépendance .

Par exemple, si vous écrivez un test pour un système qui télécharge des fichiers sur un site Web, vous pouvez créer un mock qui accepte un fichier et que vous pouvez utiliser pour affirmer que le fichier téléchargé est correct. Ou, à une plus petite échelle, il est courant d'utiliser un modèle d'objet pour vérifier que le système testé appelle des méthodes spécifiques de l'objet simulé.

Les simulacres sont liés à test d'interaction, qui est une méthodologie de test spécifique. Les personnes qui préfèrent tester état du système plutôt que interactions du système utiliseront des simulacres avec modération, voire pas du tout.

Double test

Les faux, les moignons et les fous appartiennent tous à la catégorie doubles tests. Un double test est un objet ou un système que vous utilisez dans un test au lieu de autre chose. La plupart des tests logiciels automatisés impliquent l'utilisation de tests en double, d'une manière ou d'une autre. Certains autres types de doublons de test incluent valeurs factices , espions et I/O trous noirs .

2
Daniel Pryden

Si vous connaissez Arrange-Act-Assert, une façon d'expliquer la différence qui peut vous être utile entre le stub et le simulacre est que les stubs appartiennent à la section d'arrangement, comme ils organisent l'état de saisie, la section affirmation comme ils sont pour affirmer des résultats contre.

Les nuls ne font rien. Ils servent uniquement à remplir des listes de paramètres afin d'éviter des erreurs non définies ou nulles. Ils existent également pour satisfaire le vérificateur de types dans des langages strictement typés, de sorte que vous puissiez être autorisés à compiler et à exécuter.

2
Sammi

la chose que vous affirmez à ce sujet s'appelle un objet mock et tout ce qui a simplement aidé l'exécution du test est un stub .

1
Arezoo Bagherzadi

stub et fake sont des objets en ce sens qu'ils peuvent faire varier leur réponse en fonction de paramètres d'entrée. La principale différence entre eux est qu'un Fake est plus proche d'une implémentation réelle que d'un stub. Les stubs contiennent essentiellement des réponses codées en dur à une demande attendue. Voyons un exemple: 

public class MyUnitTest {

 @Test
 public void testConcatenate() {
  StubDependency stubDependency = new StubDependency();
  int result = stubDependency.toNumber("one", "two");
  assertEquals("onetwo", result);
 }
}

public class StubDependency() {
 public int toNumber(string param) {
  if (param == “one”) {
   return 1;
  }
  if (param == “two”) {
   return 2;
  }
 }
}

Un mock est une avancée des faux et des moignons. Les simulacres offrent les mêmes fonctionnalités que les talons mais sont plus complexes. Ils peuvent avoir des règles définies pour eux qui déterminent dans quel ordre les méthodes de leur API doivent être appelées. La plupart des simulacres peuvent savoir combien de fois une méthode a été appelée et peuvent réagir en fonction de ces informations. Les simulacres connaissent généralement le contexte de chaque appel et peuvent réagir différemment selon les situations. Pour cette raison, les simulacres exigent une connaissance de la classe à laquelle ils se moquent. un stub ne peut généralement pas suivre le nombre de fois qu'une méthode a été appelée ni dans quel ordre une séquence de méthodes a été appelée. Une maquette ressemble à:

public class MockADependency {

 private int ShouldCallTwice;
 private boolean ShouldCallAtEnd;
 private boolean ShouldCallFirst;

 public int StringToInteger(String s) {
  if (s == "abc") {
   return 1;
  }
  if (s == "xyz") {
   return 2;
  }
  return 0;
 }

 public void ShouldCallFirst() {
  if ((ShouldCallTwice > 0) || ShouldCallAtEnd)
   throw new AssertionException("ShouldCallFirst not first thod called");
  ShouldCallFirst = true;
 }

 public int ShouldCallTwice(string s) {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallTwice called before ShouldCallFirst");
  if (ShouldCallAtEnd)
   throw new AssertionException("ShouldCallTwice called after ShouldCallAtEnd");
  if (ShouldCallTwice >= 2)
   throw new AssertionException("ShouldCallTwice called more than twice");
  ShouldCallTwice++;
  return StringToInteger(s);
 }

 public void ShouldCallAtEnd() {
  if (!ShouldCallFirst)
   throw new AssertionException("ShouldCallAtEnd called before ShouldCallFirst");
  if (ShouldCallTwice != 2) throw new AssertionException("ShouldCallTwice not called twice");
  ShouldCallAtEnd = true;
 }

}

La ressource suivante m'a beaucoup appris, avec une excellente explication de Robert C. Martin (oncle Bob):

The Little Mocker sur le blog Clean Code

Il explique les différences et les subtilités de

  • nuls
  • test double
  • bouts
  • des espions
  • (vrai) se moque
  • faux

Il mentionne également Martin Fowler et explique un peu l'historique des tests de logiciels.

En aucun cas je n'ai l'intention de répondre à cette question avec ce lien comme étant une vraie réponse. Cependant, cela m'a permis de mieux comprendre les concepts de moquerie et d'espionnage, alors je réponds en espérant que cela aidera davantage de gens.

0
Erik