python file a.py
には、2つのクラスA
およびB
が含まれます。
class A(object):
def method_a(self):
return "Class A method a"
class B(object):
def method_b(self):
a = A()
print a.method_a()
ユニットテストをしたいmethod_b
B
をモックすることにより、クラスA
で。ファイルの内容は次のとおりですtesta.py
この目的のために:
import unittest
import mock
import a
class TestB(unittest.TestCase):
@mock.patch('a.A')
def test_method_b(self, mock_a):
mock_a.method_a.return_value = 'Mocked A'
b = a.B()
b.method_b()
if __== '__main__':
unittest.main()
Mocked A
出力。しかし、私が得るのは:
<MagicMock name='A().method_a()' id='4326621392'>
どこで間違っていますか?
@mock.patch('a.A')
の場合、テスト中のコードのクラスA
を_mock_a
_に置き換えています。
_B.method_b
_で、a = A()
を設定します。現在はa = mock_a()
です。つまり、a
は_return_value
_の_mock_a
_です。この値を指定していないため、通常のMagicMock
です。これも設定されていないため、メソッドを呼び出すときにデフォルトの応答(まだ別のMagicMock
)を取得します。
代わりに、_return_value
_ of _mock_a
_に適切なメソッドを設定し、次のいずれかの方法で実行できます。
_mock_a().method_a.return_value = 'Mocked A'
# ^ note parentheses
_
または、おそらくより明示的に:
_mock_a.return_value.method_a.return_value = 'Mocked A'
_
a.method_a()
がモックメソッドをトリガーしたため、コードは_a = A
_(インスタンスを作成せずにクラスを割り当てる)の場合に機能します。
pytest with mocker Fixture を好む。 pytestとmockerを使用した同じテストを次に示します。
_import a
class TestB:
def test_method_b(self, mocker):
mock_A = mocker.MagicMock(name='A', spec=a.A)
mocker.patch('a.A', new=mock_A)
mock_A.return_value.method_a.return_value = 'Mocked A'
b = a.B()
b.method_b()
_
私がテストを書いた方法は、テスト自体よりも興味深いと思うかもしれません-構文を手伝うために python library を作成しました。
体系的にあなたの問題に取り組んだ方法は次のとおりです。
あなたが望むテストと私のヘルパーライブラリから始めます:
_import a
from mock_autogen.pytest_mocker import PytestMocker
class TestB:
def test_method_b(self, mocker):
# this would output the mocks we need
print(PytestMocker(a).mock_classes().prepare_asserts_calls().generate())
# your original test, without the mocks
b = a.B()
b.method_b()
_
これでテストはあまり機能しませんが、印刷出力は便利です。
_# mocked classes
mock_A = mocker.MagicMock(name='A', spec=a.A)
mocker.patch('a.A', new=mock_A)
mock_B = mocker.MagicMock(name='B', spec=a.B)
mocker.patch('a.B', new=mock_B)
# calls to generate_asserts, put this after the 'act'
import mock_autogen
print(mock_autogen.generator.generate_asserts(mock_A, name='mock_A'))
print(mock_autogen.generator.generate_asserts(mock_B, name='mock_B'))
_
今、私はB()
の呼び出しの前にA
の単一のモックを配置し、その後に_generate_asserts
_セクションを配置しています(前の印刷は不要なので、削除しました)それ):
_def test_method_b(self, mocker):
# mocked classes
mock_A = mocker.MagicMock(name='A', spec=a.A)
mocker.patch('a.A', new=mock_A)
# your original test, without the mocks
b = a.B()
b.method_b()
# calls to generate_asserts, put this after the 'act'
import mock_autogen
print(mock_autogen.generator.generate_asserts(mock_A, name='mock_A'))
_
このテストの実行後、いくつかの貴重な入力が得られました。
_assert 1 == mock_A.call_count
mock_A.assert_called_once_with()
mock_A.return_value.method_a.assert_called_once_with()
mock_A.return_value.method_a.return_value.__str__.assert_called_once_with()
_
最初の2行は、A
モックがパラメーターなしで1回初期化されたことを確認します。 3行目は_method_a
_が呼び出されたことを確認しますが、4行目が最も役立つ場合があります。
_mock_A.return_value.method_a.return_value.__str__.assert_called_once_with()
_
_method_a
_の戻り値がstr
(print
関数による)で適用されていることがわかります。それを希望の文字列に置き換えるのはとても簡単です:
_mock_A.return_value.method_a.return_value = 'Mocked A'
_
そして、それが私が上記の完全なテスト方法に到達した方法です。