web-dev-qa-db-fra.com

Mocking python fonction basée sur les arguments d'entrée

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é. Le side_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)]

http://www.voidspace.org.uk/python/mock/mock.html#calling

152
Amber

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))
10
Shubham Chaudhary

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!

2
Manu

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
1
caleb

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`
0
Tomasz Bartkowiak