diff --git a/CHANGES.rst b/CHANGES.rst index 3b94132cb..23e6dace3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -30,6 +30,7 @@ Removed Fixed +++++ * Made type annotations stronger and removed most of the ``typing.Any`` usages and ``# type: ignore`` annotations. `#658 `_ +* Ignore ``unittest.mock.patch``-provided arguments when resolving step fixtures. `#330 `_ Security ++++++++ diff --git a/src/pytest_bdd/utils.py b/src/pytest_bdd/utils.py index 0a3405363..f5e1c5ad1 100644 --- a/src/pytest_bdd/utils.py +++ b/src/pytest_bdd/utils.py @@ -10,6 +10,8 @@ from typing import TYPE_CHECKING, Callable, TypeVar, cast, overload from weakref import WeakKeyDictionary +from _pytest.compat import num_mock_patch_args + if TYPE_CHECKING: from _pytest.config import Config from _pytest.pytester import RunResult @@ -29,9 +31,10 @@ def get_required_args(func: Callable[..., object]) -> list[str]: :return: A list of argument names. """ params = signature(func).parameters.values() - return [ + required_args = [ param.name for param in params if param.kind == param.POSITIONAL_OR_KEYWORD and param.default is param.empty ] + return required_args[num_mock_patch_args(func) :] def get_caller_module_locals(stacklevel: int = 1) -> dict[str, object]: diff --git a/tests/steps/test_common.py b/tests/steps/test_common.py index fad26c9d9..ea653ea6b 100644 --- a/tests/steps/test_common.py +++ b/tests/steps/test_common.py @@ -122,6 +122,40 @@ def _(foo, expected_value): assert foo2 == "test bar" +def test_step_function_accepts_unittest_mock_patch_argument(pytester): + pytester.makefile( + ".feature", + mock_patch=textwrap.dedent( + """\ + Feature: Mock patch arguments + Scenario: A step uses unittest.mock.patch as a decorator + Then the patched function is called + """ + ), + ) + pytester.makepyfile( + textwrap.dedent( + """\ + from pathlib import Path + from unittest.mock import patch + + from pytest_bdd import scenarios, then + + scenarios("mock_patch.feature") + + + @then("the patched function is called") + @patch("pathlib.Path.cwd") + def _(cwd_mock): + Path.cwd() + cwd_mock.assert_called_once_with() + """ + ) + ) + result = pytester.runpytest() + result.assert_outcomes(passed=1) + + def test_step_functions_same_parser(pytester): pytester.makefile( ".feature",