しばらくの間、pythonに Mock を使用していました。
今、関数をモックしたい状況があります
def foo(self, my_param):
#do something here, assign something to my_result
return my_result
通常、これをモックする方法は次のとおりです(fooがオブジェクトの一部であると仮定)
self.foo = MagicMock(return_value="mocked!")
たとえfoo()を数回呼び出すと、
self.foo = MagicMock(side_effect=["mocked once", "mocked twice!"])
現在、入力パラメーターに特定の値があるときに固定値を返したいという状況に直面しています。 「my_param」が「something」に等しいとすると、「my_cool_mock」を返します
これは pythonのmockito で利用できるようです
when(dummy).foo("something").thenReturn("my_cool_mock")
私はモックで同じことを達成する方法を探していましたが、成功しませんでしたか?
何か案は?
side_effect
が関数の場合、その関数が返すものはすべて、モックの呼び出しを返します。side_effect
関数は、モックと同じ引数で呼び出されます。これにより、入力に基づいて呼び出しの戻り値を動的に変更できます。>>> 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)]
メソッドが複数回呼び出されたPython Mockオブジェクト
解決策は、独自の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
それはトリックです
副作用には関数(lambda functionの場合もあります)がかかるため、単純な場合には次のように使用できます。
m = MagicMock(side_effect=(lambda x: x+1))
私はここで「入力引数に基づいて関数をモックする方法」を探して、ついにこれを解決して簡単な補助関数を作成しました:
def mock_responses(responses, default_response=None):
return lambda input: responses[input] if input in responses else default_response
今:
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'
これが誰かを助けることを願っています!
別の方法を示すためだけに:
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
@mock.patch.object
を使用することもできます:
モジュールmy_module.py
がpandas
を使用してデータベースから読み取るとし、pd.read_sql_table
メソッド(table_name
を引数として取る)をモックしてこのモジュールをテストするとします。
できることは、(テスト内で)db_mock
メソッドを作成して、提供された引数に応じて異なるオブジェクトを返すことです。
def db_mock(**kwargs):
if kwargs['table_name'] == 'table_1':
# return some DataFrame
Elif kwargs['table_name'] == 'table_2':
# return some other DataFrame
テスト機能では、次のことを行います。
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`