私がテストしているクラスは、依存関係として別のクラス(CUTのinitメソッドに渡されるインスタンス)を持っています。 Python Mockライブラリを使用して、このクラスをモックアウトします。
私が持っているものは次のようなものです:
mockobj = Mock(spec=MyDependencyClass)
mockobj.methodfromdepclass.return_value = "the value I want the mock to return"
assertTrue(mockobj.methodfromdepclass(42), "the value I want the mock to return")
cutobj = ClassUnderTest(mockobj)
これは問題ありませんが、「methodfromdepclass」はパラメータ化されたメソッドであるため、methodfromdepclassに渡される引数に応じて異なる値を返す単一のモックオブジェクトを作成します。
このパラメーター化された動作が必要な理由は、異なる値(mockobjから返される値によって生成される値)を含むClassUnderTestの複数のインスタンスを作成することです。
ちょっと私が考えていること(これはもちろん機能しません):
mockobj = Mock(spec=MyDependencyClass)
mockobj.methodfromdepclass.ifcalledwith(42).return_value = "you called me with arg 42"
mockobj.methodfromdepclass.ifcalledwith(99).return_value = "you called me with arg 99"
assertTrue(mockobj.methodfromdepclass(42), "you called me with arg 42")
assertTrue(mockobj.methodfromdepclass(99), "you called me with arg 99")
cutinst1 = ClassUnderTest(mockobj, 42)
cutinst2 = ClassUnderTest(mockobj, 99)
# now cutinst1 & cutinst2 contain different values
この "ifcalledwith"のセマンティクスをどのように実現しますか?
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
少し甘い:
mockobj.method.side_effect = lambda x: {123: 100, 234: 10000}[x]
または複数の引数の場合:
mockobj.method.side_effect = lambda *x: {(123, 234): 100, (234, 345): 10000}[x]
またはデフォルト値で:
mockobj.method.side_effect = lambda x: {123: 100, 234: 10000}.get(x, 20000)
または両方の組み合わせ:
mockobj.method.side_effect = lambda *x: {(123, 234): 100, (234, 345): 10000}.get(x, 20000)
そして陽気に私たちは行きます。
私は自分でテストをしていたときにこれに遭遇しました。 methodfromdepclass()への呼び出しをキャプチャすることを気にせず、何かを返すためにそれを必要とするだけであれば、以下で十分です。
def makeFakeMethod(mapping={}):
def fakeMethod(inputParam):
return mapping[inputParam] if inputParam in mapping else MagicMock()
return fakeMethod
mapping = {42:"Called with 42", 59:"Called with 59"}
mockobj.methodfromdepclass = makeFakeMethod(mapping)
パラメーター化されたバージョンは次のとおりです。
def makeFakeMethod():
def fakeMethod(param):
return "Called with " + str(param)
return fakeMethod
here の場合と同様に、side_effect
in nittest.mock.Mock を使用することもできます @mock.patch.object
with new_callable
。これにより、オブジェクトの属性にモックオブジェクトをパッチできます。
モジュール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`