From 4384dd8c151f5647a0d6e0e81bc544ea6dcc4001 Mon Sep 17 00:00:00 2001 From: PerchunPak Date: Sun, 19 Jun 2022 14:47:45 +0300 Subject: [PATCH 1/9] Add `async_stub` method Which actually the same as `stub` but creates async stub. --- src/pytest_mock/plugin.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/pytest_mock/plugin.py b/src/pytest_mock/plugin.py index 088e5ce..52661c0 100644 --- a/src/pytest_mock/plugin.py +++ b/src/pytest_mock/plugin.py @@ -159,6 +159,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) -> unittest.mock.AsyncMock: + """ + 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, From 2644564591229af552e969aebfeed37f489ae2fa Mon Sep 17 00:00:00 2001 From: PerchunPak Date: Sun, 19 Jun 2022 14:51:48 +0300 Subject: [PATCH 2/9] Add reference in docs --- docs/usage.rst | 4 ++++ 1 file changed, 4 insertions(+) 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. From 18583546a2cfb9a03a20b09e6f786efe3dab9d06 Mon Sep 17 00:00:00 2001 From: PerchunPak Date: Fri, 24 Jun 2022 19:16:18 +0300 Subject: [PATCH 3/9] Update CHANGELOG.rst --- CHANGELOG.rst | 6 ++++++ 1 file changed, 6 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) ------------------ From 86bd0a809516ceb5d70a17f4a33ba0892e680005 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 24 Jun 2022 13:17:15 -0300 Subject: [PATCH 4/9] Include async_stub only in Python 3.8+ --- src/pytest_mock/plugin.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/pytest_mock/plugin.py b/src/pytest_mock/plugin.py index 52661c0..17a2a64 100644 --- a/src/pytest_mock/plugin.py +++ b/src/pytest_mock/plugin.py @@ -159,18 +159,20 @@ 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) -> unittest.mock.AsyncMock: - """ - Create a async stub method. It accepts any arguments. Ideal to register to - callbacks in tests. + if sys.version_info[:2] >= 3.8: + + def async_stub(self, name: Optional[str] = None) -> unittest.mock.AsyncMock: + """ + 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), - ) + :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: """ From 8729c3368e35524796c0f59d9cd973c302496d8e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 24 Jun 2022 13:19:24 -0300 Subject: [PATCH 5/9] Fix version --- src/pytest_mock/plugin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pytest_mock/plugin.py b/src/pytest_mock/plugin.py index 17a2a64..72c02b6 100644 --- a/src/pytest_mock/plugin.py +++ b/src/pytest_mock/plugin.py @@ -159,7 +159,7 @@ def stub(self, name: Optional[str] = None) -> unittest.mock.MagicMock: self.mock_module.MagicMock(spec=lambda *args, **kwargs: None, name=name), ) - if sys.version_info[:2] >= 3.8: + if sys.version_info[:2] >= (3, 8): def async_stub(self, name: Optional[str] = None) -> unittest.mock.AsyncMock: """ From 62aa75d53072045871523d18a8813b8c401639bd Mon Sep 17 00:00:00 2001 From: PerchunPak Date: Fri, 24 Jun 2022 19:21:30 +0300 Subject: [PATCH 6/9] Add simple test --- tests/test_pytest_mock.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_pytest_mock.py b/tests/test_pytest_mock.py index b8edbdb..7e7a7ef 100644 --- a/tests/test_pytest_mock.py +++ b/tests/test_pytest_mock.py @@ -232,6 +232,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(not hasattr(unittest.mock, "AsyncMock"), reason="This Python version doesn't have `AsyncMock`.") + def test_async_stub_type(self) -> None: + assert type(mocker.async_stub()) == unittest.mock.AsyncMock def test_instance_method_spy(mocker: MockerFixture) -> None: From 98f33f679226b633856ff10a603cd4c4c0f69acc Mon Sep 17 00:00:00 2001 From: PerchunPak Date: Fri, 24 Jun 2022 19:30:15 +0300 Subject: [PATCH 7/9] Fix import in tests --- tests/test_pytest_mock.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_pytest_mock.py b/tests/test_pytest_mock.py index 7e7a7ef..a5d3c54 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): @@ -233,9 +236,9 @@ def test_failure_message_with_no_name(self, mocker: MagicMock) -> None: def test_failure_message_with_name(self, mocker: MagicMock, name: str) -> None: self.__test_failure_message(mocker, name=name) - @pytest.mark.skipif(not hasattr(unittest.mock, "AsyncMock"), reason="This Python version doesn't have `AsyncMock`.") + @pytest.mark.skipif(sys.version_info[:2] < (3, 8), reason="This Python version doesn't have `AsyncMock`.") def test_async_stub_type(self) -> None: - assert type(mocker.async_stub()) == unittest.mock.AsyncMock + assert type(mocker.async_stub()) == AsyncMock def test_instance_method_spy(mocker: MockerFixture) -> None: From 197889cb4e89417ef00ab5074be40ffc75f403da Mon Sep 17 00:00:00 2001 From: PerchunPak Date: Fri, 24 Jun 2022 19:55:04 +0200 Subject: [PATCH 8/9] Support standalone `mock` package --- src/pytest_mock/plugin.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/pytest_mock/plugin.py b/src/pytest_mock/plugin.py index 72c02b6..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,20 +165,18 @@ def stub(self, name: Optional[str] = None) -> unittest.mock.MagicMock: self.mock_module.MagicMock(spec=lambda *args, **kwargs: None, name=name), ) - if sys.version_info[:2] >= (3, 8): - - def async_stub(self, name: Optional[str] = None) -> unittest.mock.AsyncMock: - """ - Create a async stub method. It accepts any arguments. Ideal to register to - callbacks in tests. + 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), - ) + :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: """ From 3e70ddac925fc227a5f7f446638563026d057343 Mon Sep 17 00:00:00 2001 From: PerchunPak Date: Fri, 24 Jun 2022 19:55:37 +0200 Subject: [PATCH 9/9] Fix test to correctly assert type --- tests/test_pytest_mock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_pytest_mock.py b/tests/test_pytest_mock.py index a5d3c54..eaf48da 100644 --- a/tests/test_pytest_mock.py +++ b/tests/test_pytest_mock.py @@ -237,8 +237,8 @@ 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) -> None: - assert type(mocker.async_stub()) == AsyncMock + def test_async_stub_type(self, mocker: MockerFixture) -> None: + assert isinstance(mocker.async_stub(), AsyncMock) def test_instance_method_spy(mocker: MockerFixture) -> None: