diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d116714..1d04769 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ Releases ======== +UNRELEASED +---------- + +* `#650 `_: Entering a mock created by ``mocker.patch`` as a context manager now returns its ``__enter__.return_value`` instead of always ``None``, while still issuing the warning about using mocks as context managers. + 3.17.0 ------ diff --git a/src/pytest_mock/plugin.py b/src/pytest_mock/plugin.py index 71e5c2b..92bae25 100644 --- a/src/pytest_mock/plugin.py +++ b/src/pytest_mock/plugin.py @@ -312,14 +312,20 @@ def _start_patch( # check if `mocked` is actually a mock object, as depending on autospec or target # parameters `mocked` can be anything if hasattr(mocked, "__enter__") and warn_on_mock_enter: - mocked.__enter__.side_effect = lambda: warnings.warn( - "Mocks returned by pytest-mock do not need to be used as context managers. " - "The mocker fixture automatically undoes mocking at the end of a test. " - "This warning can be ignored if it was triggered by mocking a context manager. " - "https://pytest-mock.readthedocs.io/en/latest/usage.html#usage-as-context-manager", - PytestMockWarning, - stacklevel=5, - ) + + def warn_on_enter() -> Any: + warnings.warn( + "Mocks returned by pytest-mock do not need to be used as context managers. " + "The mocker fixture automatically undoes mocking at the end of a test. " + "This warning can be ignored if it was triggered by mocking a context manager. " + "https://pytest-mock.readthedocs.io/en/latest/usage.html#usage-as-context-manager", + PytestMockWarning, + stacklevel=5, + ) + # Let the mock return its ``return_value`` as usual. + return self.mock_module.DEFAULT + + mocked.__enter__.side_effect = warn_on_enter return mocked def object( diff --git a/tests/test_pytest_mock.py b/tests/test_pytest_mock.py index 8921e6d..d02d656 100644 --- a/tests/test_pytest_mock.py +++ b/tests/test_pytest_mock.py @@ -1837,6 +1837,23 @@ def my_func(): mocker.patch.object(dummy_module, "MyContext") assert isinstance(my_func(), mocker.MagicMock) + def test_patching_a_context_manager_object(self, mocker: MockerFixture) -> None: + """The warning should not change what entering the patched object returns.""" + + class dummy_module: + pool: Any = None + + pool = mocker.patch.object(dummy_module, "pool") + with pytest.warns(PytestMockWarning), dummy_module.pool as entered: + pass + assert entered is pool.__enter__.return_value + + connection = object() + pool.__enter__.return_value = connection + with pytest.warns(PytestMockWarning), dummy_module.pool as entered: + pass + assert entered is connection + def test_patch_context_manager(self, mocker: MockerFixture) -> None: """Test that no warnings are issued when an object patched with patch.context_manager is used as a context manager (#221)"""