這個測驗讓我發瘋,我無法弄清楚。
mocker.patchMagicMock在我的實際測驗中回傳一個(如預期的那樣)。但是,當它呼叫模塊并且模擬我想要修補的類時,它回傳的是 a NonCallableMagicMock,而不是 a MagicMock。因此,當我執行 an 時assert_called_with,它失敗并拋出錯誤,因為兩者不同。
我做的補丁不正確嗎?我確保修補在使用它的模塊的命名空間內的類,而不是該類所在的實際模塊的命名空間。NonCallableMagicMock回傳a 的事實使我相信我正在修補正確的目標。
因此,如果我打補丁正確,那么為什么會出現此錯誤?如何斷言該函式是使用MyQuery作為引數的實體呼叫的?
我有以下代碼結構:
.
├── main.py
├── src
│ ├── handler
│ │ └── my_query_handler.py
│ ├── query
│ │ └── my_query.py
│ └── repo
│ └── my_repo.py
└── tests
└── handler
└── test_my_query_handler.py
所有檔案的代碼如下:
my_query_handler.py
from src.query.my_query import MyQuery
from src.repo.my_repo import MyRepo
class MyQueryHandler:
def handle(self, repo: MyRepo):
query = MyQuery(value_one="Hello", value_two="World")
result = repo.exec(query=query)
return result
my_query.py
class MyQuery:
_value_one: str
_value_two: str
def __init__(self, value_one: str, value_two: str):
self._value_one = value_one
self._value_two = value_two
def get_query(self) -> str:
return f"{self._value_one} {self._value_two}"
my_repo.py
from src.query.my_query import MyQuery
class MyRepo:
def exec(self, query: MyQuery):
return query.get_query()
test_my_query_handler.py
import pytest
from src.repo.my_repo import MyRepo
from src.handler.my_query_handler import MyQueryHandler
from src.handler.my_query_handler import MyQuery
from unittest.mock import MagicMock
class TestMyQueryHandler:
@pytest.fixture
def mock_query(self, mocker):
namespace = f"{MyQueryHandler.__module__}.{MyQuery.__name__}"
return mocker.patch(namespace, autospec=True)
def test_my_query_handler(self, mock_query):
expected_value = 'Hello World'
mock_repo = MagicMock(spec=MyRepo)
mock_repo.exec.return_value = expected_value
handler = MyQueryHandler()
result = handler.handle(mock_repo)
mock_repo.exec.assert_called_with(query=mock_query)
assert result == expected_value
主檔案
from src.handler.my_query_handler import MyQueryHandler
from src.repo.my_repo import MyRepo
handler = MyQueryHandler()
repo = MyRepo()
print(handler.handle(repo))
當我運行這些測驗時,回傳的模擬mock_query是一個MagicMock:
<MagicMock name='MyQuery' spec='MyQuery' id='4365116176'
但是,當我運行測驗時,修補模塊時,它會創建一個 NonCallableMagicMock
<NonCallableMagicMock name='MyQuery()' spec='MyQuery' id='4365118992'>
當我執行以下操作時會生成以下錯誤 assert_called_with
_______________________________________________________ TestMyQueryHandler.test_my_query_handler _______________________________________________________
__wrapped_mock_method__ = <function NonCallableMock.assert_called_with at 0x105b407a0>, args = (<MagicMock name='mock.exec' id='4375790672'>,)
kwargs = {'query': <MagicMock name='MyQuery' spec='MyQuery' id='4391261264'>}, __tracebackhide__ = True
msg = "Expected call: exec(query=<MagicMock name='MyQuery' spec='MyQuery' id='4391261264'>)\nActual call: exec(query=<NonCal...)' spec='MyQuery' id='4391264144'>}\n ? ^"
__mock_self = <MagicMock name='mock.exec' id='4375790672'>, actual_args = ()
actual_kwargs = {'query': <NonCallableMagicMock name='MyQuery()' spec='MyQuery' id='4391264144'>}
introspection = "\nKwargs:\nassert {'query': <No...'4391264144'>} == {'query': <Ma...'4391261264'>}\n Differing items:\n {'query': <...)' spec='MyQuery' id='4391264144'>}\n ? ^"
@py_assert2 = None, @py_assert1 = False
def assert_wrapper(
__wrapped_mock_method__: Callable[..., Any], *args: Any, **kwargs: Any
) -> None:
__tracebackhide__ = True
try:
> __wrapped_mock_method__(*args, **kwargs)
env/lib/python3.7/site-packages/pytest_mock/plugin.py:414:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_mock_self = <MagicMock name='mock.exec' id='4375790672'>, args = (), kwargs = {'query': <MagicMock name='MyQuery' spec='MyQuery' id='4391261264'>}
expected = ((), {'query': <MagicMock name='MyQuery' spec='MyQuery' id='4391261264'>})
_error_message = <function NonCallableMock.assert_called_with.<locals>._error_message at 0x105bb6440>
actual = call(query=<NonCallableMagicMock name='MyQuery()' spec='MyQuery' id='4391264144'>), cause = None
def assert_called_with(_mock_self, *args, **kwargs):
"""assert that the mock was called with the specified arguments.
Raises an AssertionError if the args and keyword args passed in are
different to the last call to the mock."""
self = _mock_self
if self.call_args is None:
expected = self._format_mock_call_signature(args, kwargs)
raise AssertionError('Expected call: %s\nNot called' % (expected,))
def _error_message():
msg = self._format_mock_failure_message(args, kwargs)
return msg
expected = self._call_matcher((args, kwargs))
actual = self._call_matcher(self.call_args)
if expected != actual:
cause = expected if isinstance(expected, Exception) else None
> raise AssertionError(_error_message()) from cause
E AssertionError: Expected call: exec(query=<MagicMock name='MyQuery' spec='MyQuery' id='4391261264'>)
E Actual call: exec(query=<NonCallableMagicMock name='MyQuery()' spec='MyQuery' id='4391264144'>)
../../../.pyenv/versions/3.7.10/lib/python3.7/unittest/mock.py:878: AssertionError
During handling of the above exception, another exception occurred:
self = <test_my_query_handler.TestMyQueryHandler object at 0x105bc9b90>, mock_query = <MagicMock name='MyQuery' spec='MyQuery' id='4391261264'>
def test_my_query_handler(self, mock_query):
expected_value = 'Hello World'
mock_repo = MagicMock(spec=MyRepo)
mock_repo.exec.return_value = expected_value
handler = MyQueryHandler()
result = handler.handle(mock_repo)
> mock_repo.exec.assert_called_with(query=mock_query)
E AssertionError: Expected call: exec(query=<MagicMock name='MyQuery' spec='MyQuery' id='4391261264'>)
E Actual call: exec(query=<NonCallableMagicMock name='MyQuery()' spec='MyQuery' id='4391264144'>)
E
E pytest introspection follows:
E
E Kwargs:
E assert {'query': <No...'4391264144'>} == {'query': <Ma...'4391261264'>}
E Differing items:
E {'query': <NonCallableMagicMock name='MyQuery()' spec='MyQuery' id='4391264144'>} != {'query': <MagicMock name='MyQuery' spec='MyQuery' id='4391261264'>}
E Full diff:
E - {'query': <MagicMock name='MyQuery' spec='MyQuery' id='4391261264'>}
E ? ^^
E {'query': <NonCallableMagicMock name='MyQuery()' spec='MyQuery' id='4391264144'>}
E ? ^
tests/handler/test_my_query_handler.py:34: AssertionError
uj5u.com熱心網友回復:
如果您使用autospec,并且您正在模擬一個類,則模擬的行為就像一個類。您不能在類上呼叫實體方法,因為您需要一個實體,對于您return_value在類模擬上使用的模擬。
所以要修復你的代碼,你只需要使用實體模擬而不是類模擬,或者通過調整夾具:
@pytest.fixture
def mock_query(self, mocker):
namespace = f"{MyQueryHandler.__module__}.{MyQuery.__name__}"
return mocker.patch(namespace, autospec=True).return_value
或者通過調整呼叫者:
result = handler.handle(mock_repo)
mock_repo.exec.assert_called_with(query=mock_query.return_value)
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/385172.html
標籤:Python 蟒蛇-3.x 单元测试 pytest 魔术师
