Python moqueur, il y a quelque chose de non-intuitif sur vous. Vous n’êtes pas facile à maîtriser et il faut plusieurs » Ah, maintenant je comprends! »quelques instants avant que vous soyez mieux compris., Vous êtes un outil puissant et fantastique pour permettre des tests unitaires automatisés, mais je connais de nombreux développeurs qui « se débrouillent” sur les connaissances qu’ils ont et écrire ces tests prend plus de temps qu’il ne devrait.
Il y a beaucoup d’articles sur la moquerie, et oui, j’en écris maintenant un autre. Cependant, je veux vraiment essayer de l’expliquer clairement, en particulier ceux « Aha! »des moments qui ont fait avancer ma compréhension.
Tout d’abord, qu’est-ce que la moquerie? Voici une citation des documents:
mock est une bibliothèque pour tester en Python., Il vous permet de remplacer des parties de votre système en cours de test par des objets simulés et de faire des affirmations sur la façon dont ils ont été utilisés.
Donc, « il permet de remplacer les pièces de vos systèmes”, mais quelles pièces? Il s’avère que vous pouvez vous moquer à peu près de tout ce que vous pouvez définir, comme:
- fonctions
- classes
- objets
Vous les remplacez par des « objets simulés” qui sont des instances de la classe Mock ou MagicMock.,
Vous « faites ensuite des assertions” sur cette instance fictive, ce qui est un moyen de vérifier que l’instance fictive a été utilisée comme vous l’attendiez.
Dans cet article, je vais couvrir les bases du remplacement de pièces de vos systèmes, mais je ne couvrirai pas les affirmations.
commençons par un exemple simple. Je vais créer un nouveau module appelé simple.py et, dans ce module, je vais définir une fonction appelée simple_function qui retourne une chaîne de caractères comme ceci:
def simple_function():
return "You have called simple_function"
Maintenant, je vais créer un deuxième module appelé use_simple.py., Dans ce module, je vais importer la maquette paquet de unittest et importer le module simple:
from unittest import mockimport simple
Ensuite, je vais écrire deux fonctions, l’une qui appelle simple_function normalement et celui qui se moque de l’appel à elle. Notez que normalement, vous le feriez dans un contexte de test, mais je supprime délibérément cela afin que je puisse me concentrer sur la partie moqueuse. La première fonction:
def use_simple_function():
result = simple.simple_function()
print(result)use_simple_function()
Comme vous pouvez le voir, il se contente d’afficher le résultat de simple_function., Plus précisément, la sortie est:
You have called simple_function
Pour la seconde fonction, pour se moquer simple_function je vais utiliser la maquette.décorateur de patch. Ce décorateur vous permet de spécifier ce que vous voulez moquer en lui passant une chaîne dans le format ‘package.module.FunctionName’., Dans notre exemple, il n’y a pas de package, mais le module s’appelle simple et la fonction s’appelle simple_function, donc le décorateur ressemblera à:
@mock.patch('simple.simple_function')
Nous devons ensuite écrire notre deuxième fonction comme suit:
@mock.patch('simple.simple_function')
def mock_simple_function():
Je l’ai appelé mock_simple_function, mais il peut Il nous manque un paramètre ici parce que lorsque vous décorez une fonction avec @mock.patch il passera une instance de la classe MagicMock (un objet MagicMock) qui est utilisée pour remplacer la fonction dont vous vous moquez., Cela ressemblera donc à ceci:
@mock.patch('simple.simple_function')
def mock_simple_function(mock_simple_func):
Donc en spécifiant @mock.patch (‘simple.simple_function ‘ ’ Je dis que je veux remplacer simple_function par l’objet MagicMock affecté au paramètre appelé mock_simple_func.
Dans un premier temps, étoffons la fonction en imprimant mock_simple_func, puis appelez-la:
Lors de l’exécution, la sortie est:
<MagicMock name='simple_function'>
Qui est un objet MagicMock qui sera appelé au lieu de simple_function., Il est important de noter que l’attribut name de l’objet est la chose qui est moquée, et l’id est un numéro unique pour l’objet.
Si nous imprimons maintenant également simple_function (mais ne l’appelons pas):
Qui produit la sortie:
<MagicMock name='simple_function'>
<MagicMock name='simple_function'>
Vous pouvez voir que, à partir des ID correspondants, mock_simple_func est le même objet MagicMock que simple_function.
jusqu’ici tout va bien. Nous nous sommes moqués de simple_function. Pour être explicite, simple_function a été moqué car il a été remplacé par un objet MagicMock.,
Ajoutons les lignes de la première fonction:
Lors de l’exécution, la sortie est:
Comme vous pouvez le voir à partir de la troisième ligne de sortie lorsque simple_function est réellement appelé, il crée un objet MagicMock différent. Rappelez-vous que puisque nous nous sommes moqués de simple_function, il s’agit en fait d’un objet MagicMock, donc lorsque nous appelons simple_function, nous appelons en fait l’objet MagicMock!
Pour moi, c’est là que la confusion s’installe.
- Pourquoi un nouvel objet MagicMock est-il créé?
- Et comment pouvez-vous appeler un objet, de toute façon?, Normalement, vous obtenez « TypeError: ‘ * * * * l’objet n’est pas appelable” lorsque vous essayez.
Arrêtons-nous une minute pour regarder de plus près MagicMock.
MagicMock a magic dans son nom car il a des implémentations par défaut de la plupart des méthodes magiques python. Qu’est-ce qu’une méthode magique python que vous demandez? Ce sont toutes les fonctions Python spéciales qui ont un double trait de soulignement au début et à la fin de leur nom. Vous pouvez trouver une liste ici. Celui qui vous intéresse ici est __call__., La fonction d’appel est décrite comme:
Appelée lorsqu’un objet est appelé en tant que fonction
Donc, si une classe implémente cette fonction, vous pouvez créer une instance de la classe, puis appeler cette instance comme une fonction, c’est-à-dire :
a fonction call __est appelée. L’implémentation de cette fonction par MagicMock crée une nouvelle simulation!
Donc, lorsque nous appelons simple_function (), un nouveau mock est créé et renvoyé. Non seulement cela, mais nous avons le concept de moqueries parents et enfants., Quand un mock en crée un autre, il devient le parent et le nouveau créé l’enfant. Cette simulation d’enfant pourrait créer d’autres moqueries afin que vous vous retrouviez avec une hériterie d’objets simulés.
Point d’apprentissage 1: Lorsqu’un objet MagicMock est appelé comme une fonction, par défaut, il crée et renvoie un nouvel objet MagicMock.
revenons à notre exemple. Jusqu’à présent, nous nous sommes juste moqués de simple_function mais nous n’avons rien fait d’autre que d’imprimer les MagicMocks qui sont créés à la suite. Disons que je voulais changer ce qui a été renvoyé par simple_function (), comment ferais-je cela?, Je le ferais en utilisant la propriété return_value de MagicMock comme ceci (je vais également supprimer les deux premières impressions car elles ne sont plus nécessaires):
Lors de l’exécution, la sortie est:
You have mocked simple_function
Comme vous pouvez le voir lorsque simple_function() est maintenant appelé MagicMock return_value est retourné au lieu de créer un
Point d’apprentissage 2: Pour changer ce qui est renvoyé lorsqu’un objet MagicMock est appelé comme une fonction, définissez return_value.
Si vous voulez faire plus que changer la valeur de retour, vous pouvez utiliser le MagicMock.fonction side_effect., Il vous permet de spécifier une fonction entièrement nouvelle qui sera appelée à la place de celle dont vous vous moquez. Nous allons le faire. Tout d’abord, je vais définir une nouvelle fonction qui sera le side_effect (notez qu’il doit avoir le même ensemble de paramètres que la fonction dont vous vous moquez):
def side_effect_function():
return "SKABLAM!"
Maintenant, nous pouvons définir une nouvelle fonction qui utilise cet effet secondaire comme suit:
Tout est identique à la fonction mock_simple_func return_value.,
La sortie est:
SKABLAM!
Un bon cas d’utilisation pour l’utilisation de l’effet secondaire est que si vous voulez tester un flux d’erreur et, par conséquent, vous voulez soulever une exception dans votre test. Je vais changer la fonction side_effect_function pour déclencher une erreur à la place:
def side_effect_function():
raise FloatingPointError("A disastrous floating point error has occurred")
Maintenant, la sortie est (j’ai supprimé une partie de la trace pour plus de clarté):
Traceback (most recent call last):
File "use_simple.py”, line 18, in side_effect_function
raise FloatingPointError("A disastrous floating point error has occurred”)
FloatingPointError: A disastrous floating point error has occurred
Point d’apprentissage 3: Pour faire plus que simplement changer la valeur de retour
Classes moqueuses
Jusqu’à présent, nous venons de nous moquer d’une fonction, mais que diriez-vous d’une classe?, Définissons une classe dans le simple.py appelé SimpleClass avec une méthode appelée explode:
class SimpleClass(object):
def explode(self):
return "KABOOM!"
Maintenant dans use_simple.py écrivons une fonction qui utilise SimpleClass, puis appelons cette fonction:
def use_simple_class():
inst = simple.SimpleClass()
print(inst.explode())use_simple_class()
Lors de l’exécution, la sortie est:
KABOOM!
Définissons maintenant une deuxième fonction et choisissons de se moquer de l’intégralité de simple_class en utilisant @mock.patch decorator:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
Comme lorsque vous vous moquez d’une fonction, le @mock.,patch decorator passe un objet MagicMock qui remplace la classe dont vous vous moquez dans la fonction qu’il décore. Dans ce cas, l’objet MagicMock est affecté à l’argument mock_class.
Pour le moment, je vais juste imprimer le MagicMock objet, puis exécutez la fonction:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
print(mock_class)mock_simple_class()
Lors de l’exécution à la sortie est:
<MagicMock name='SimpleClass'>
Comme avec les moqueries d’une fonction d’une MagicMock objet est créé et passé en., Permet maintenant d’imprimer SimpleClass afin que vous puissiez voir qu’il a été moqué et remplacé par le même objet MagicMock:
@mock.patch("simple.SimpleClass")
def mock_simple_class(mock_class):
print(mock_class)
print(simple.SimpleClass)
La sortie est maintenant:
<MagicMock name='SimpleClass'>
<MagicMock name='SimpleClass'>
Créons une instance de SimpleClass, c’est-à-dire appelez SimpleClass (), puis imprimons-la. Rappelez-vous, puisque nous nous sommes moqués de SimpleClass, ce qui se passera réellement, c’est que nous appellerons l’objet MagicMock comme une fonction:
La sortie est:
Donc comme prévu, appeler SimpleClass() et donc appeler l’objet MagicMock en tant que fonction crée un nouvel objet MagicMock.,
Jusqu’à présent, se moquer d’une classe a été un peu comme se moquer d’une fonction. Cependant, en réalité, lorsque nous définissons une classe, nous créerons alors des objets en utilisant cette classe et ce sont ces objets le plus souvent que nous voulons vraiment nous moquer. La question est, comment vous moquer d’une instance créée à partir d’une classe? Eh bien, lorsque vous vous moquez d’une classe et que, par conséquent, un objet MagicMock est créé, si vous vous référez à return_value sur cet objet MagicMock, il crée un nouvel objet MagicMock qui représente l’instance de la classe créée. Blimey! C’était une phrase et demi!!! C’était certainement l’un de mes « Aha!, »moments, mais même l’écrire ne le rend pas clair. Imprimons return_value afin que vous puissiez voir ce que je veux dire:
La sortie est:
La troisième ligne provient de l’impression de l’instance de la classe créée, et la quatrième ligne provient de l’impression de la return_value de l’objet MagicMock passé à la fonction. Comme vous pouvez le voir, ils font référence au même objet MagicMock.
Point d’apprentissage 4: Lorsque vous vous moquez d’une classe, un objet MagicMock est créé. Lorsque vous créez une instance de cette classe, un nouvel objet MagicMock est créé., Lorsque vous faites référence à return_value de l’objet MagicMock de cette classe, vous obtenez le même objet MagicMock qui a été créé lors de la création de l’instance de cette classe.
« Ahhhhh, pourquoi est-ce important?!?!?” Je vous entends pleurer!
« Il est important si vous voulez vous moquer de la fonction exploser » Je souffle en retour!
La prochaine question que vous devez vous poser est: est la méthode que je veux simuler une méthode de classe ou d’une méthode d’instance? Parce que la façon dont vous modifiez la return_value de la méthode sera différente en fonction de la réponse.,
Si la méthode était une méthode de classe, vous définiriez la return_value comme suit:
MagicMockObject.ClassMethodName.return_value = "A delightful return value"
Mais s’il s’agissait d’une méthode d’instance, vous feriez:
MagicMockObject.return_value.InstanceMethodName.return_value = "A daring return value"
Dans cet esprit, changeons la return_value de explode, qui est une méthode d’instance (je supprimerai clarity) :
Dans le code ci-dessus, mock_class.return_value renvoie l’objet MagicMock qui représente l’instance de SimpleClass.
mock_class.return_value.,explode renvoie l’objet MagicMock qui représente la méthode explode de l’instance de SimpleClass.
Définissant donc la valeur return_value de mock_class.return_value.explode définit la valeur return_value de la méthode explode de l’instance de SimpleClass.
La sortie est:
BOO!
Tout comme lorsque vous vous moquez d’une fonction, vous pouvez également définir side_effect pour les méthodes de classes et d’objets.
J’espère que cet article vous a aidé à comprendre un peu plus les moqueries. Veuillez le recommander si vous l’avez aimé et n’hésitez pas à laisser une réponse car j’aimerais avoir de vos nouvelles.