wake-up-neo.com

Mocking python Funktion basierend auf Eingabeargumenten

Wir haben Mock für python für eine Weile verwendet.

Jetzt haben wir eine Situation, in der wir eine Funktion verspotten wollen

def foo(self, my_param):
    #do something here, assign something to my_result
    return my_result

Normalerweise wäre der Weg, dies zu verspotten (vorausgesetzt, foo ist Teil eines Objekts)

self.foo = MagicMock(return_value="mocked!")

Selbst wenn ich foo () ein paar Mal aufrufe, kann ich es verwenden

self.foo = MagicMock(side_effect=["mocked once", "mocked twice!"])

Jetzt sehe ich mich einer Situation gegenüber, in der ich einen festen Wert zurückgeben möchte, wenn der Eingabeparameter einen bestimmten Wert hat. Also, wenn wir sagen, "my_param" ist gleich "etwas", dann möchte ich "my_cool_mock" zurückgeben

Dies scheint verfügbar zu sein auf Mockito für Python

when(dummy).foo("something").thenReturn("my_cool_mock")

Ich habe gesucht, wie ich mit Mock dasselbe erreichen kann, ohne Erfolg?

Irgendwelche Ideen?

Ob side_effect ist eine Funktion. Was auch immer diese Funktion zurückgibt, ruft das Mock-Return auf. Das side_effect Funktion wird mit den gleichen Argumenten wie der Mock aufgerufen. Auf diese Weise können Sie den Rückgabewert des Aufrufs basierend auf der Eingabe dynamisch variieren:

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

Wie unter Python Mock-Objekt mit mehrfach aufgerufenen Methoden angegeben

Eine Lösung besteht darin, meinen eigenen Nebeneffekt zu schreiben

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

Das macht den Trick

Die Nebenwirkung hat eine Funktion (die auch eine Lambda-Funktion sein kann). Für einfache Fälle können Sie also Folgendes verwenden:

m = MagicMock(side_effect=(lambda x: x+1))
10

Ich bin hier gelandet und habe nach "wie man eine Funktion auf der Basis von Eingabeargumenten verspottet" gesucht. Schließlich habe ich dieses Problem gelöst und eine einfache Aux-Funktion erstellt:

def mock_responses(responses, default_response=None):
  return lambda input: responses[input] if input in responses else default_response

Jetzt:

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'

Hoffe das wird jemandem helfen!

2
Manu

Nur um einen anderen Weg zu zeigen:

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

Sie können auch @mock.patch.object Verwenden:

Angenommen, ein Modul my_module.py Verwendet pandas zum Lesen aus einer Datenbank, und wir möchten dieses Modul testen, indem wir die Methode pd.read_sql_table Verspotten (die table_name Als Argument verwendet) ).

Sie können (innerhalb Ihres Tests) eine db_mock - Methode erstellen, die abhängig vom angegebenen Argument verschiedene Objekte zurückgibt:

def db_mock(**kwargs):
    if kwargs['table_name'] == 'table_1':
        # return some DataFrame
    Elif kwargs['table_name'] == 'table_2':
        # return some other DataFrame

In Ihrer Testfunktion machen Sie dann:

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