Nous utilisons Mock depuis python depuis un moment.
Maintenant, nous avons une situation dans laquelle nous voulons nous moquer d'une fonction
def foo(self, my_param):
#do something here, assign something to my_result
return my_result
Normalement, le moyen de se moquer de cela serait (en supposant que foo fasse partie d'un objet)
self.foo = MagicMock(return_value="mocked!")
Même si j'appelle foo () plusieurs fois, je peux utiliser
self.foo = MagicMock(side_effect=["mocked once", "mocked twice!"])
Maintenant, je suis confronté à une situation dans laquelle je veux renvoyer une valeur fixe lorsque le paramètre d'entrée a une valeur particulière. Donc, si disons que "mon_param" est égal à "quelque chose" alors je veux retourner "mon_cool_mock"
Cela semble être disponible sur mockito pour python
when(dummy).foo("something").thenReturn("my_cool_mock")
J'ai cherché comment atteindre le même objectif avec Mock sans succès?
Des idées?
Si
side_effect
est une fonction, alors quoi qu’elle retourne, c’est ce qui appelle le retour simulé. Leside_effect
La fonction est appelée avec les mêmes arguments que la simulation. Cela vous permet de faire varier la valeur de retour de l'appel de manière dynamique, en fonction de l'entrée:>>> def side_effect(value): ... return value + 1 ... >>> m = MagicMock(side_effect=side_effect) >>> m(1) 2 >>> m(2) 3 >>> m.mock_calls [call(1), call(2)]
Comme indiqué à objet Python Mock avec une méthode appelée plusieurs fois
Une solution consiste à écrire mon propre side_effect
def my_side_effect(*args, **kwargs):
if args[0] == 42:
return "Called with 42"
Elif args[0] == 43:
return "Called with 43"
Elif kwarg['foo'] == 7:
return "Foo is seven"
mockobj.mockmethod.side_effect = my_side_effect
Ça fait l'affaire
L'effet secondaire prend une fonction (qui peut aussi être une fonction lambda), vous pouvez donc utiliser pour les cas simples:
m = MagicMock(side_effect=(lambda x: x+1))
J'ai fini ici par chercher "comment se moquer d'une fonction basée sur des arguments d'entrée" et j'ai finalement résolu ce problème en créant une fonction aux simple:
def mock_responses(responses, default_response=None):
return lambda input: responses[input] if input in responses else default_response
Maintenant:
my_mock.foo.side_effect = mock_responses({'x': 42, 'y': [1,2,3]})
my_mock.goo.side_effect = mock_responses({'hello': 'world'},
default_response='hi')
...
my_mock.foo('x') # => 42
my_mock.foo('y') # => [1,2,3]
my_mock.foo('unknown') # => None
my_mock.goo('hello') # => 'world'
my_mock.goo('ey') # => 'hi'
J'espère que cela aidera quelqu'un!
Juste pour montrer une autre façon de le faire:
def mock_isdir(path):
return path in ['/var/log', '/var/log/Apache2', '/var/log/Tomcat']
with mock.patch('os.path.isdir') as os_path_isdir:
os_path_isdir.side_effect = mock_isdir
Vous pouvez également utiliser @mock.patch.object
:
Supposons qu'un module my_module.py
Utilise pandas
pour lire dans une base de données et nous voudrions tester ce module en moquant la méthode pd.read_sql_table
(Qui prend comme argument table_name
).
Ce que vous pouvez faire est de créer (dans votre test) une méthode db_mock
Qui retourne différents objets en fonction de l'argument fourni:
def db_mock(**kwargs):
if kwargs['table_name'] == 'table_1':
# return some DataFrame
Elif kwargs['table_name'] == 'table_2':
# return some other DataFrame
Dans votre fonction de test, vous effectuez ensuite:
import my_module as my_module_imported
@mock.patch.object(my_module_imported.pd, "read_sql_table", new_callable=lambda: db_mock)
def test_my_module(mock_read_sql_table):
# You can now test any methods from `my_module`, e.g. `foo` and any call this
# method does to `read_sql_table` will be mocked by `db_mock`, e.g.
ret = my_module_imported.foo(table_name='table_1')
# `ret` is some DataFrame returned by `db_mock`