diff --git a/changelog/5606.bugfix.rst b/changelog/5606.bugfix.rst new file mode 100644 index 00000000000..82332ba9972 --- /dev/null +++ b/changelog/5606.bugfix.rst @@ -0,0 +1,2 @@ +Fixed internal error when test functions were patched with objects that cannot be compared +for truth values against others, like ``numpy`` arrays. diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index d238061b4ab..baa84bd4b87 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -67,8 +67,17 @@ def num_mock_patch_args(function): mock_modules = [sys.modules.get("mock"), sys.modules.get("unittest.mock")] if any(mock_modules): sentinels = [m.DEFAULT for m in mock_modules if m is not None] + + def check_is_sentinel(o): + """Check if the given object is a mock sentinel by identity to avoid possible + problematic comparisons (#5606)""" + for sentinel in sentinels: + if o is sentinel: + return True + return False + return len( - [p for p in patchings if not p.attribute_name and p.new in sentinels] + [p for p in patchings if not p.attribute_name and check_is_sentinel(p.new)] ) return len(patchings) diff --git a/testing/python/integration.py b/testing/python/integration.py index 0b87fea3327..3ea95b3d55c 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -187,6 +187,37 @@ def test_hello_mock(self, abspath): reprec = testdir.inline_run() reprec.assertoutcome(passed=2) + def test_mock_sentinel_check_against_numpy_like(self, testdir): + """Ensure our function that detects mock arguments compares against sentinels using + identity to circumvent objects which can't be compared with equality against others + in a truth context, like with numpy arrays (#5606). + """ + testdir.makepyfile( + dummy=""" + class NumpyLike: + def __init__(self, value): + self.value = value + + def __eq__(self, other): + raise ValueError("like numpy, cannot compare against others for truth") + + FOO = NumpyLike(10) + """ + ) + testdir.syspathinsert() + testdir.makepyfile( + """ + from unittest.mock import patch + import dummy + class Test(object): + @patch("dummy.FOO", new=dummy.NumpyLike(50)) + def test_hello(self): + assert dummy.FOO.value == 50 + """ + ) + reprec = testdir.inline_run() + reprec.assertoutcome(passed=1) + def test_mock(self, testdir): pytest.importorskip("mock", "1.0.1") testdir.makepyfile(