From 7cc4cec0531b7751f4e73cbbb73009c169d3c531 Mon Sep 17 00:00:00 2001 From: PerchunPak Date: Fri, 24 Jun 2022 21:23:02 +0300 Subject: [PATCH] Add `async_stub` method (#296) --- CHANGELOG.rst | 6 ++++++ docs/usage.rst | 4 ++++ src/pytest_mock/plugin.py | 19 +++++++++++++++++++ tests/test_pytest_mock.py | 7 +++++++ 4 files changed, 36 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1987e9b..c6d265a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,12 @@ Releases ======== +3.8.0 (unreleased) +------------------ +* Add ``MockerFixture.async_mock`` method. Thanks `@PerchunPak`_ for the PR. + +.. _@PerchunPak: https://github.com/PerchunPak + 3.7.0 (2022-01-28) ------------------ diff --git a/docs/usage.rst b/docs/usage.rst index 7cbe21d..f457c74 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -110,3 +110,7 @@ It may receive an optional name that is shown in its ``repr``, useful for debugg foo(stub) stub.assert_called_once_with('foo', 'bar') + +.. seealso:: + + ``async_stub`` method, which actually the same as ``stub`` but makes async stub. diff --git a/src/pytest_mock/plugin.py b/src/pytest_mock/plugin.py index 088e5ce..293e76e 100644 --- a/src/pytest_mock/plugin.py +++ b/src/pytest_mock/plugin.py @@ -27,6 +27,12 @@ _T = TypeVar("_T") +if sys.version_info[:2] > (3, 7): + AsyncMockType = unittest.mock.AsyncMock +else: + import mock + AsyncMockType = mock.AsyncMock + class PytestMockWarning(UserWarning): """Base class for all warnings emitted by pytest-mock.""" @@ -159,6 +165,19 @@ def stub(self, name: Optional[str] = None) -> unittest.mock.MagicMock: self.mock_module.MagicMock(spec=lambda *args, **kwargs: None, name=name), ) + def async_stub(self, name: Optional[str] = None) -> AsyncMockType: + """ + Create a async stub method. It accepts any arguments. Ideal to register to + callbacks in tests. + + :param name: the constructed stub's name as used in repr + :return: Stub object. + """ + return cast( + unittest.mock.AsyncMock, + self.mock_module.AsyncMock(spec=lambda *args, **kwargs: None, name=name), + ) + class _Patcher: """ Object to provide the same interface as mock.patch, mock.patch.object, diff --git a/tests/test_pytest_mock.py b/tests/test_pytest_mock.py index b8edbdb..eaf48da 100644 --- a/tests/test_pytest_mock.py +++ b/tests/test_pytest_mock.py @@ -26,6 +26,9 @@ # Python 3.8 changed the output formatting (bpo-35500), which has been ported to mock 3.0 NEW_FORMATTING = sys.version_info >= (3, 8) +if sys.version_info[:2] >= (3, 8): + from unittest.mock import AsyncMock + @pytest.fixture def needs_assert_rewrite(pytestconfig): @@ -232,6 +235,10 @@ def test_failure_message_with_no_name(self, mocker: MagicMock) -> None: @pytest.mark.parametrize("name", (None, "", "f", "The Castle of aaarrrrggh")) def test_failure_message_with_name(self, mocker: MagicMock, name: str) -> None: self.__test_failure_message(mocker, name=name) + + @pytest.mark.skipif(sys.version_info[:2] < (3, 8), reason="This Python version doesn't have `AsyncMock`.") + def test_async_stub_type(self, mocker: MockerFixture) -> None: + assert isinstance(mocker.async_stub(), AsyncMock) def test_instance_method_spy(mocker: MockerFixture) -> None: