Qu'est-ce que la moquerie? .
unit-testing
mocking
masoud ramezani
la source
la source
Réponses:
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.
la source
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:
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
1
et5
fait 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 device
et 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éelleLa 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é enid
. 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:
la source
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).
la source
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
la source
La simulation génère des pseudo-objets qui simulent le comportement des objets réels pour les tests
la source
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.
la source
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.
la source
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/
la source