gh-100690: Raise an AttributeError when the assert_ prefix is forgotten when using Mock (#100691)

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.
This commit is contained in:
Christian Klein 2023-01-06 19:38:50 +01:00 committed by GitHub
parent 9ffbc58f5c
commit 1d4d677d1c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 4 deletions

View file

@ -653,7 +653,7 @@ class NonCallableMock(Base):
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.")
@ -1062,6 +1062,10 @@ class NonCallableMock(Base):
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
@ -1231,9 +1235,11 @@ class Mock(CallableMixin, NonCallableMock):
`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