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. Dasside_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)]
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))
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!
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
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`