m.aseert_foo_call()
with self.assertRaisesRegex(AttributeError, msg):
m.assrt_foo_call()
+ with self.assertRaisesRegex(AttributeError, msg):
+ m.called_once_with()
+ with self.assertRaisesRegex(AttributeError, msg):
+ m.called_once()
+ with self.assertRaisesRegex(AttributeError, msg):
+ m.has_calls()
+
+ class Foo(object):
+ def called_once(self):
+ pass
+
+ def has_calls(self):
+ pass
+
+ m = Mock(spec=Foo)
+ m.called_once()
+ m.has_calls()
+
+ m.called_once.assert_called_once()
+ m.has_calls.assert_called_once()
+
m = Mock(unsafe=True)
m.assert_foo_call()
m.assret_foo_call()
m.asert_foo_call()
m.aseert_foo_call()
m.assrt_foo_call()
+ m.called_once()
+ m.called_once_with()
+ m.has_calls()
# gh-100739
def test_mock_safe_with_spec(self):
elif _is_magic(name):
raise AttributeError(name)
if not self._mock_unsafe and (not self._mock_methods or name not in self._mock_methods):
- if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')):
+ if name.startswith(('assert', 'assret', 'asert', 'aseert', 'assrt')) or name in ATTRIB_DENY_LIST:
raise AttributeError(
f"{name!r} is not a valid assertion. Use a spec "
f"for the mock if {name!r} is meant to be an attribute.")
return f"\n{prefix}: {safe_repr(self.mock_calls)}."
+# Denylist for forbidden attribute names in safe mode
+ATTRIB_DENY_LIST = {name.removeprefix("assert_") for name in dir(NonCallableMock) if name.startswith("assert_")}
+
+
class _AnyComparer(list):
"""A list which checks if it contains a call which may have an
argument of ANY, flipping the components of item and self from
`return_value` attribute.
* `unsafe`: By default, accessing any attribute whose name starts with
- *assert*, *assret*, *asert*, *aseert* or *assrt* will raise an
- AttributeError. Passing `unsafe=True` will allow access to
- these attributes.
+ *assert*, *assret*, *asert*, *aseert*, or *assrt* raises an AttributeError.
+ Additionally, an AttributeError is raised when accessing
+ attributes that match the name of an assertion method without the prefix
+ `assert_`, e.g. accessing `called_once` instead of `assert_called_once`.
+ Passing `unsafe=True` will allow access to these attributes.
* `wraps`: Item for the mock object to wrap. If `wraps` is not None then
calling the Mock will pass the call through to the wrapped object
--- /dev/null
+``Mock`` objects which are not unsafe will now raise an
+``AttributeError`` when accessing an attribute that matches the name
+of an assertion but without the prefix ``assert_``, e.g. accessing
+``called_once`` instead of ``assert_called_once``.
+This is in addition to this already happening for accessing attributes
+with prefixes ``assert``, ``assret``, ``asert``, ``aseert``,
+and ``assrt``.