[PYTHON] Touchez la maquette et le talon

Aperçu

Dans cette phrase

Explique. En Python3, MagicMock est inclus dans le module standard unittest.mock. En Python2, vous pouvez l'utiliser en installant le paquetage fictif avec pip install mock.

Jouez avec MagicMock

Lancez l'interpréteur Python et créez un objet MagicMock

$ python3.6
Python 3.6.0 (default, Feb 27 2017, 00:03:01)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from unittest.mock import MagicMock
>>> f = MagicMock()

Je suppose que ce f est une fonction et j'essaye de l'appeler

>>> f(1)
<MagicMock name='mock()' id='4381867704'>

Je peux l'appeler même si je ne l'ai pas défini comme une fonction. C'est étrange. La valeur de retour est également MagicMock, mais je m'en fiche et je l'appellerai plus loin

>>> f(2)
<MagicMock name='mock()' id='4381867704'>
>>> f(1, 2, 3)
<MagicMock name='mock()' id='4381867704'>

Je pourrais l'appeler librement même si je changeais la valeur de l'argument et le nombre d'arguments. Je suis surpris. Si vous essayez d'évaluer f.call_args_list ici,

>>> f.call_args_list
[call(1), call(2), call(1, 2, 3)]

Je l'ai appelé trois fois depuis que j'ai créé MagicMock. Vous pouvez voir qu'il est enregistré avec les arguments. c'est intéressant.

Ce n'est pas la seule fonctionnalité de MagicMock. Créons à nouveau une instance MagicMock, définissons la valeur sur f.return_value, puis appelons f.

>>> f = MagicMock()
>>> f.return_value = 5
>>> f(1, 2)
5
>>> f(1)
5
>>> f()
5

Quel que soit l'argument que vous donnez, la valeur définie sera renvoyée. De cette façon, vous pouvez remplacer la valeur de retour de la fonction par la valeur de votre choix. Dans ce cas également, la manière dont l'appel a été effectué est enregistrée.

>>> f.call_args_list
[call(1, 2), call(1), call()]

Il existe d'autres fonctionnalités, mais laissons cela de côté pour l'instant et voyons comment elles peuvent vous aider.

Enregistrement d'appel

Supposons que vous ayez une fonction push_if_even () qui prend un entier, appelle push () si elle est paire, et ne fait rien si elle est impaire. Plus précisément, le code est le suivant.

def push():
    pass

def push_if_even(x):
    """Reçoit un entier et pousse s'il est pair()Appel"""
    if x % 2 == 0:
        push()

Supposons maintenant que vous souhaitiez écrire un test pour voir si ce code est correctement implémenté. Comme nous l'avons vu précédemment, MagicMock garde un enregistrement de ce qui a été appelé en tant que fonction, nous en profitons donc. Vous pouvez le tester en procédant comme suit:

from unittest.mock import MagicMock

push = MagicMock()
push_if_even(1)
assert len(push.call_args_list) == 0

push = MagicMock()
push_if_even(0)
assert len(push.call_args_list) == 1

print("success")

Vous pouvez également écrire comme suit

push = MagicMock()
push_if_even(1)
push.asset_not_called()

push = MagicMock()
push_if_even(0)
push.assert_called_once()

Jetons un coup d'œil à ce qui se passe lorsque l'assertion échoue.

>>> from unittest.mock import MagicMock
>>> push = MagicMock()
>>> push.assert_called_once()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py", line 795, in assert_called_once
    raise AssertionError(msg)
AssertionError: Expected 'mock' to have been called once. Called 0 times.
>>> push()
<MagicMock name='mock()' id='4356157392'>
>>> push.assert_not_called()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python3/3.6.0/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/mock.py", line 777, in assert_not_called
    raise AssertionError(msg)
AssertionError: Expected 'mock' to not have been called. Called 1 times.

Contrôle de la valeur de retour

Une fonction qui renvoie True si aujourd'hui est dimanche, False sinon ʻis_sunday`

from datetime import date

def today():
    return date.today()

def is_sunday():
    return today().weekday() == 6

Supposons que vous souhaitiez tester qui fonctionne correctement. Si aujourd'hui n'est pas dimanche, vous pouvez l'appeler une fois et voir qu'il renvoie False, puis attendre le dimanche suivant et le rappeler pour voir qu'il renvoie True. Cependant, il faudra des jours pour terminer le test. Si vous ne pouvez pas attendre cela, utilisez la capacité de MagicMock à contrôler la valeur de retour.

from unittest.mock import MagicMock

today = MagicMock()
today.return_value = date(2020, 2, 1)
assert is_sunday() == False

today = MagicMock()
today.return_value = date(2020, 2, 2)
assert is_sunday() == True

print("success")

Si tel est le cas, vous pouvez le tester en un instant. C'est pratique.

Résumé

Après avoir vu la nature de MagicMock,

J'ai implémenté le code de test en utilisant la fonction de. Le test des fonctions qui affectent ou sont affectées par l'environnement est plus difficile à écrire que le test de fonctions simples, mais vous pouvez vous moquer de l'impact sur l'environnement ou stuber l'impact de l'environnement. Nous avons vu qu'il est facile de créer un code de test reproductible en le remplaçant.

Dans cet exemple, j'ai simplement écrasé la fonction, mais le module fictif a un élément pratique appelé patch, qui permet de remplacer facilement la fonction uniquement pendant le test, puis de la restaurer. Utilisez-le lors de la rédaction de tests.

Recommended Posts

Touchez la maquette et le talon
Mock urllib2 et unittest
J'hériterai et remplacerai la classe
L'histoire de Python et l'histoire de NaN
Exécutez Pylint et lisez les résultats
Installation et désinstallation du paquet d'oeufs
Touchez l'objet du réseau neuronal
Jugez l'extension et téléchargez l'image
Comprendre et implémenter l'algorithme Tonelli-Shanks (1)
L'histoire de la création d'une caméra sonore avec Touch Designer et ReSpeaker