mirror of https://github.com/python/cpython
Restrict use of Mock objects as specs (GH-31090)
Follow-on to https://github.com/python/cpython/pull/25326 This covers cases where mock objects are passed directly to spec.
This commit is contained in:
parent
8726067ace
commit
6394e981ad
|
@ -489,6 +489,9 @@ class NonCallableMock(Base):
|
|||
|
||||
def _mock_add_spec(self, spec, spec_set, _spec_as_instance=False,
|
||||
_eat_self=False):
|
||||
if _is_instance_mock(spec):
|
||||
raise InvalidSpecError(f'Cannot spec a Mock object. [object={spec!r}]')
|
||||
|
||||
_spec_class = None
|
||||
_spec_signature = None
|
||||
_spec_asyncs = []
|
||||
|
@ -2789,6 +2792,7 @@ FunctionTypes = (
|
|||
|
||||
|
||||
file_spec = None
|
||||
open_spec = None
|
||||
|
||||
|
||||
def _to_stream(read_data):
|
||||
|
@ -2845,8 +2849,12 @@ def mock_open(mock=None, read_data=''):
|
|||
import _io
|
||||
file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO))))
|
||||
|
||||
global open_spec
|
||||
if open_spec is None:
|
||||
import _io
|
||||
open_spec = list(set(dir(_io.open)))
|
||||
if mock is None:
|
||||
mock = MagicMock(name='open', spec=open)
|
||||
mock = MagicMock(name='open', spec=open_spec)
|
||||
|
||||
handle = MagicMock(spec=file_spec)
|
||||
handle.__enter__.return_value = handle
|
||||
|
|
|
@ -226,6 +226,14 @@ class MockTest(unittest.TestCase):
|
|||
with self.assertRaisesRegex(InvalidSpecError,
|
||||
"Cannot spec attr 'B' as the spec_set "):
|
||||
mock.patch.object(A, 'B', spec_set=A.B).start()
|
||||
with self.assertRaisesRegex(InvalidSpecError,
|
||||
"Cannot spec attr 'B' as the spec_set "):
|
||||
mock.patch.object(A, 'B', spec_set=A.B).start()
|
||||
with self.assertRaisesRegex(InvalidSpecError, "Cannot spec a Mock object."):
|
||||
mock.Mock(A.B)
|
||||
with mock.patch('builtins.open', mock.mock_open()):
|
||||
mock.mock_open() # should still be valid with open() mocked
|
||||
|
||||
|
||||
def test_reset_mock(self):
|
||||
parent = Mock()
|
||||
|
|
|
@ -130,8 +130,8 @@ class WithTest(unittest.TestCase):
|
|||
|
||||
c = C()
|
||||
|
||||
with patch.object(c, 'f', autospec=True) as patch1:
|
||||
with patch.object(c, 'f', autospec=True) as patch2:
|
||||
with patch.object(c, 'f') as patch1:
|
||||
with patch.object(c, 'f') as patch2:
|
||||
c.f()
|
||||
self.assertEqual(patch2.call_count, 1)
|
||||
self.assertEqual(patch1.call_count, 0)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Mocks can no longer be provided as the specs for other Mocks. As a result, an already-mocked object cannot be passed to `mock.Mock()`. This can uncover bugs in tests since these Mock-derived Mocks will always pass certain tests (e.g. isinstance) and builtin assert functions (e.g. assert_called_once_with) will unconditionally pass.
|
Loading…
Reference in New Issue