Qu'est-ce que la moquerie?

Réponses:

599

Prologue: Si vous recherchez le nom simulé dans le dictionnaire, vous constaterez que l'une des définitions du mot est une imitation .


La moquerie est principalement utilisée dans les tests unitaires. Un objet testé peut avoir des dépendances avec d'autres objets (complexes). Pour isoler le comportement de l'objet, vous souhaitez remplacer les autres objets par des simulations simulant le comportement des objets réels. Cela est utile si les objets réels ne peuvent pas être incorporés dans le test unitaire.

En bref, se moquer, c'est créer des objets qui simulent le comportement d'objets réels.


Parfois, vous voudrez peut-être faire la distinction entre la moquerie et le tronçonnage . Il peut y avoir un certain désaccord sur ce sujet, mais ma définition d'un talon est un objet simulé "minimal". Le stub implémente juste assez de comportement pour permettre à l'objet sous test d'exécuter le test.

Une maquette est comme un talon mais le test vérifiera également que l'objet testé appelle la maquette comme prévu. Une partie du test consiste à vérifier que la maquette a été utilisée correctement.

Pour donner un exemple: Vous pouvez stub une base de données en implémentant une structure en mémoire simple pour stocker des enregistrements. L'objet testé peut ensuite lire et écrire des enregistrements dans le talon de la base de données pour lui permettre d'exécuter le test. Cela pourrait tester un comportement de l'objet non lié à la base de données et le talon de base de données serait inclus juste pour laisser le test s'exécuter.

Si vous souhaitez plutôt vérifier que l'objet testé teste des données spécifiques dans la base de données, vous devrez vous moquer de la base de données. Votre test comprendrait alors des assertions sur ce qui a été écrit dans la maquette de la base de données.

Martin Liversage
la source
18
C'est une bonne réponse, mais elle limite inutilement le concept de moquerie aux objets . Remplacer "objet" par "unité" le rendrait plus général.
Rogério
1
Je comprends la différence entre le talon et la maquette. La seule chose est que si vous testez vos cas avec un talon et qu'il passe, ne pouvez-vous pas conclure que vous utilisez déjà le talon, vous n'avez donc plus besoin de la vérification ?
Honey
91

D'autres réponses expliquent ce qu'est la moquerie. Permettez-moi de vous l'expliquer avec différents exemples . Et croyez-moi, c'est en fait beaucoup plus simple que vous ne le pensez.

tl; dr C'est une instance de la classe d'origine. Il contient d'autres données injectées afin que vous évitiez de tester les parties injectées et que vous vous concentriez uniquement sur les détails d'implémentation de votre classe / fonctions.

Un exemple simple:

class Foo {
    func add (num1: Int, num2: Int) -> Int { // Line A 
        return num1 + num2 // Line B
    }
}

let unit = Foo() // unit under test
assertEqual(unit.add(1,5),6)

Comme vous pouvez le voir, je ne teste pas LineA, c'est-à-dire que je ne valide pas les paramètres d'entrée. Je ne valide pas pour voir si num1, num2 sont un entier. Je n'ai aucune affirmation contre cela.

Je teste seulement pour voir si LineB (mon implémentation ) a donné les valeurs simulées 1et 5fait ce que j'attends.

Évidemment, dans le vrai mot, cela peut devenir beaucoup plus complexe. Les paramètres peuvent être un objet personnalisé comme une personne, une adresse ou les détails d'implémentation peuvent être plus d'un seul +. Mais la logique du test serait la même.

Exemple sans codage:

Supposons que vous construisez une machine qui identifie le type et la marque des appareils électroniques pour la sécurité d'un aéroport. La machine le fait en traitant ce qu'elle voit avec sa caméra.

Maintenant, votre manager entre dans la porte et vous demande de faire un test unitaire.

Ensuite, en tant que développeur, vous pouvez soit apporter 1000 objets réels, comme un MacBook pro, Google Nexus, une banane, un iPad, etc. devant lui et tester et voir si tout fonctionne.

Mais vous pouvez également utiliser des objets fictifs , comme un MacBook pro à l'aspect identique (sans pièces internes réelles) ou une banane en plastique devant lui. Vous pouvez vous épargner en investissant dans 1000 vrais ordinateurs portables et des bananes pourries.

Le fait est que vous n'essayez pas de tester si la banane est fausse ou non. Ni tester si l'ordinateur portable est faux ou non. Tout ce que vous faites est de tester si votre machine une fois qu'il voit une banane qu'il dirait not an electronic deviceet pour un MacBook Pro , il dirait: Laptop, Apple. Pour la machine, le résultat de sa détection devrait être le même pour l'électronique fausse / moquée et l'électronique réelle

La logique mentionnée ci-dessus s'applique également aux tests unitaires du code réel. C'est une fonction qui devrait fonctionner de la même manière avec des valeurs réelles que vous obtenez à partir d' une entrée réelle (et d'interactions) ou simuléesvaleurs que vous injectez lors des tests unitaires. Et tout comme la façon dont vous vous évitez d'utiliser une vraie banane ou un MacBook, avec des tests unitaires (et des moqueries), vous vous évitez d'avoir à faire quelque chose qui oblige votre serveur à renvoyer un code d'état de 500, 403, 200, etc. (forçage votre serveur pour déclencher 500 est uniquement lorsque le serveur est en panne, tandis que 200 est lorsque le serveur est en marche. Il devient difficile d'exécuter 100 tests axés sur le réseau si vous devez constamment attendre 10 secondes entre les changements de serveur de haut en bas). Donc, à la place, vous injectez / simulez une réponse avec le code d'état 500, 200, 403, etc. et testez votre unité / fonction avec une valeur injectée / simulée.

Exemple de codage:

Supposons que vous écrivez une application iOS et que vous avez des appels réseau. Votre travail consiste à tester votre application. Tester / identifier si les appels réseau fonctionnent comme prévu n'est PAS VOTRE RESPONSABILITÉ. Il incombe à une autre partie (équipe serveur) de le tester. Vous devez supprimer cette dépendance (réseau) et continuer à tester tout votre code qui fonctionne autour d' elle.

Un appel réseau peut renvoyer différents codes d'état 404, 500, 200, 303, etc. avec une réponse JSON.

Votre application est censée fonctionner pour chacun d'eux (en cas d'erreurs, votre application doit renvoyer l'erreur attendue). Ce que vous faites avec la simulation, vous créez des réponses réseau «imaginaires - similaires aux vraies» (comme un code 200 avec un fichier JSON) et testez votre code sans « passer le véritable appel réseau et attendre votre réponse réseau». Vous codez / renvoyez manuellement la réponse réseau pour TOUS les types de réponses réseau et voyez si votre application fonctionne comme prévu. (vous ne supposez / testez jamais un 200 avec des données incorrectes, car ce n'est pas votre responsabilité, votre responsabilité est de tester votre application avec un 200 correct, ou dans le cas d'un 400, 500, vous testez si votre application génère la bonne erreur)

Cette création imaginaire, semblable au réel, est connue sous le nom de moquerie.

Pour ce faire, vous ne pouvez pas utiliser votre code d'origine (votre code d'origine n'a pas les réponses pré-insérées, non?). Vous devez y ajouter quelque chose, injecter / insérer ces données factices qui ne sont normalement pas nécessaires (ou une partie de votre classe).

Ainsi , vous créez une instance de la classe d' origine et d' ajouter tout (ici étant le réseau HTTPResponse, des données ou dans le cas d'échec, vous passez le errorString correct, HTTPResponse) , vous devez, puis tester la moquée classe.

Pour faire court, se moquer est de simplifier et de limiter ce que vous testez et aussi de vous faire nourrir ce dont dépend une classe. Dans cet exemple, vous évitez de tester les appels réseau eux-mêmes et testez plutôt si votre application fonctionne ou non comme prévu avec les sorties / réponses injectées - en se moquant des classes

Inutile de dire que vous testez chaque réponse réseau séparément.


Maintenant, une question que j'avais toujours à l'esprit était la suivante: les contrats / points de terminaison et, fondamentalement, la réponse JSON de mes API sont constamment mis à jour. Comment puis-je écrire des tests unitaires qui prennent cela en considération?

Pour en savoir plus: supposons que le modèle nécessite une clé / un champ nommé username. Vous testez cela et votre test réussit. 2 semaines plus tard, le backend change le nom de la clé en id. Vos tests réussissent toujours. droite? ou pas?

Est-ce la responsabilité du développeur backend de mettre à jour les simulations. Doit-il faire partie de notre accord qu'ils fournissent des simulations mises à jour?

La réponse au problème ci-dessus est la suivante: les tests unitaires + votre processus de développement en tant que développeur côté client doivent / devraient détecter une réponse moquée obsolète. Si vous me demandez comment? Eh bien, la réponse est:

Notre application réelle échouerait (ou n'échouerait pas mais n'aurait pas le comportement souhaité) sans utiliser les API mises à jour ... donc si cela échoue ... nous apporterons des modifications à notre code de développement. Ce qui conduit à nouveau à l'échec de nos tests ... que nous devrons corriger. (En fait, si nous devons faire le processus TDD correctement, nous ne devons écrire aucun code sur le champ à moins que nous écrivions le test pour celui-ci ... et le voyons échouer, puis allons écrire le code de développement réel pour lui.)

Tout cela signifie que le backend n'a pas à dire: «hé nous avons mis à jour les mocks» ... cela finit par se produire lors du développement / débogage de votre code. ‌ّ Parce que tout cela fait partie du processus de développement! Bien que si le backend vous fournit la réponse moquée, c'est plus facile.

Tout ce que je veux dire, c'est que (si vous ne pouvez pas automatiser la mise à jour de la réponse de l'API simulée ), une interaction humaine est requise, c'est- à- dire des mises à jour manuelles des JSON et des réunions courtes pour vous assurer que leurs valeurs sont à jour feront partie de votre processus

Cette section a été écrite grâce à une discussion lâche dans notre groupe Meetup CocoaHead


Pour les développeurs iOS uniquement:

Un très bon exemple de moquerie est cet exposé pratique axé sur le protocole de Natasha Muraschev . Passez à la minute 18:30, bien que les diapositives puissent être désynchronisées avec la vidéo réelle 🤷‍♂️

J'aime vraiment cette partie de la transcription:

Parce que cela teste ... nous voulons nous assurer que la getfonction de la Gettableest appelée, car elle peut revenir et la fonction pourrait théoriquement attribuer un tableau de produits alimentaires de n'importe où . Nous devons nous assurer qu'il est appelé;

Mon chéri
la source
3
Excellent exemple, j'ajouterais simplement que dans cet exemple particulier, la sous-classe agit comme une maquette, mais cet exemple utilise également le stubbing. Les réponses JSON codées en dur sont considérées comme des réponses tronquées. J'ajoute cela uniquement car il peut être difficile de faire la différence entre les mocks et les stubs, mais cet exemple montre clairement comment les deux peuvent être utilisés ensemble.
user3344977
Excellente explication, merci. Un petit ajustement à la question sur le changement d'API. Et si ce n'est pas votre API, vous ne faites donc pas partie du processus de développement? Je veux savoir quand ma bibliothèque cliente échoue.
ThinkDigital
@ThinkDigital Les bons fournisseurs d'API ont de bonnes notes de publication et communiquent correctement les changements, si vous n'avez pas ce canal, il est peut-être temps pour vous de vous asseoir en réunion et d'en discuter | les bons développeurs examineront toujours les changements d'API d'une nouvelle version et éviteront simplement de mettre à niveau la version d'API. Avez-vous des versions d'API? Si aucun de ceux-ci ne l'attrape, vous le découvrirez lors de l'AQ, puis mettez à jour vos tests ← devoir de toute l'équipe. → devoir un seul dev: ne devrait pas s'en soucier Il suffit de gérer le cas où le serveur renvoie une erreur, ou le serveur ne retourne pas d'erreur mais ne peut pas analyser json, ou gérer le cas correct.
Honey
Merci d'avoir répondu, @Honey! Dans mon cas, je maintiens un client pour pub.dev , qui a une API, mais cela fait gravement défaut. À tel point qu'il valait mieux faire une API en grattant leur site, que d'utiliser leur API officielle. Pour cette raison, les modifications apportées au site peuvent briser le code et ils n'auraient pas besoin de se soucier de mettre à jour quiconque dans ce cas. Le site est open source, mais c'est une chose différente de maintenir une API basée sur des changements effectués sur une base plus triviale.
ThinkDigital
32

Il y a beaucoup de réponses sur SO et de bons messages sur le Web à propos de la moquerie. Un endroit que vous voudrez peut-être commencer à chercher est le post de Martin Fowler Mocks Arn't Stubs où il discute de nombreuses idées de moqueries.

Dans un paragraphe - La simulation est une technique particulière pour permettre de tester une unité de code sans dépendre de dépendances. En général, ce qui différencie la simulation des autres méthodes, c'est que les objets simulés utilisés pour remplacer les dépendances de code permettront de définir des attentes - un objet factice saura comment il est censé être appelé par votre code et comment y répondre.


Votre question d'origine mentionnait TypeMock, j'ai donc laissé ma réponse ci-dessous:

TypeMock est le nom d'un framework de mocking commercial .

Il offre toutes les fonctionnalités des frameworks de simulation gratuits comme RhinoMocks et Moq, ainsi que des options plus puissantes.

Que vous ayez ou non besoin de TypeMock est très discutable - vous pouvez faire la plupart des moqueries que vous voudriez avec des bibliothèques de moqueries gratuites, et beaucoup soutiennent que les capacités offertes par TypeMock vous éloigneront souvent d'une conception bien encapsulée.

Comme une autre réponse l'a déclaré, «TypeMocking» n'est pas en fait un concept défini, mais pourrait être considéré comme signifiant le type de moquerie que TypeMock propose, en utilisant le profileur CLR pour intercepter les appels .Net lors de l'exécution, ce qui donne une bien plus grande capacité à simuler des objets (pas des exigences) comme avoir besoin d'interfaces ou de méthodes virtuelles).

David Hall
la source
@Masoud n'a jamais mentionné TypeMock. Sa question portait sur le "type moqueur" en général.
Peter Lillevold
4
@Peter - comme l'a dit un autre commentaire, consultez l'historique des modifications de la question. Je ne peux pas faire grand-chose si je poste une réponse et que la question d'origine est complètement modifiée.
David Hall
9

La maquette est une méthode / un objet qui simule le comportement d'une méthode / d'un objet réel de manière contrôlée. Les objets fantaisie sont utilisés dans les tests unitaires.

Souvent, une méthode en cours de test appelle d'autres services ou méthodes externes. Celles-ci sont appelées dépendances. Une fois moquées, les dépendances se comportent comme nous les avons définies.

Les dépendances étant contrôlées par des simulations, nous pouvons facilement tester le comportement de la méthode que nous avons codée. Il s'agit de tests unitaires.

À quoi servent les objets fantaisie?

Mocks vs stubs

Tests unitaires vs tests fonctionnels

Venkat Kotra
la source
7

La simulation génère des pseudo-objets qui simulent le comportement des objets réels pour les tests

LOL
la source
5

Le but des types moqueurs est de séparer les dépendances afin d'isoler le test à une unité spécifique. Les talons sont de simples substituts, tandis que les faux sont des substituts qui peuvent vérifier l'utilisation. Un cadre de simulation est un outil qui vous aidera à générer des talons et des simulations.

EDIT : Depuis la formulation originale mentionnant "type mocking", j'ai eu l'impression que cela se rapportait à TypeMock. D'après mon expérience, le terme général est simplement "moqueur". N'hésitez pas à ignorer les informations ci-dessous spécifiquement sur TypeMock.

TypeMock Isolator diffère de la plupart des autres frameworks de simulation en ce qu'il fonctionne à la volée en modifiant mon IL. Cela lui permet de se moquer des types et des instances que la plupart des autres cadres ne peuvent pas se moquer. Pour simuler ces types / instances avec d'autres frameworks, vous devez fournir vos propres abstractions et les simuler.

TypeMock offre une grande flexibilité au détriment d'un environnement d'exécution propre. En tant qu'effet secondaire de la façon dont TypeMock obtient ses résultats, vous obtiendrez parfois des résultats très étranges lors de l'utilisation de TypeMock.

Brian Rasmussen
la source
@Masoud n'a jamais mentionné TypeMock. Sa question portait sur le "type moqueur" en général.
Peter Lillevold
1
@Peter: Le libellé d'origine était "qu'est-ce que le type moqueur?".
Brian Rasmussen
Je connais. Étant donné que "type mocking" n'est pas équivalent à "TypeMock", je trouve que la vôtre et la réponse @Oded sont tout à fait différentes.
Peter Lillevold
1
@Peter: D'après mon expérience, le terme général est "moqueur", mais dans tous les cas, j'ai mis à jour ma réponse pour que cela soit clair. Merci pour la contribution.
Brian Rasmussen
3

Je pense que l'utilisation du cadre de simulation d'isolateur TypeMock serait TypeMocking.

Il s'agit d'un outil qui génère des simulations à utiliser dans les tests unitaires, sans avoir besoin d'écrire votre code en pensant à l'IoC.

Oded
la source
@Masoud n'a jamais mentionné TypeMock. Sa question portait sur le "type moqueur" en général.
Peter Lillevold
3
En fait, la question d'origine comprenait le mot "Type" avant "Mocking", mais il a ensuite été édité. C'est pourquoi certaines des réponses contiennent des informations spécifiques sur TypeMock.
Martin Liversage
1

Si votre maquette implique une requête réseau, une autre alternative est d'avoir un vrai serveur de test à frapper. Vous pouvez utiliser ce service pour générer une demande et une réponse pour vos tests. http://testerurl.com/

foobar8675
la source
J'ai juste essayé d'y accéder et cela a pris plusieurs minutes. Qui peut dire qu'il n'enregistre pas non plus secrètement les demandes? Enfin, cela pourrait être mieux en tant que commentaire :)
Kieren Johnstone
En fait, je l'ai retiré car je n'avais pas envie de le déplacer vers un hébergement gratuit. oui, cela aurait dû être un commentaire. il est open source, donc s'il y a un problème concernant les demandes de journalisation, vous pouvez exécuter le vôtre. github.com/captainchung/TesterUrl
Matthew Chung