Unspecced mocks keep passing after the real API changes, which is how mock-heavy suites rot.
A plain Mock accepts any attribute and any call signature. If the real send_email(to, subject) is renamed or gains a required argument, tests using a plain mock keep passing while production breaks.
Specs constrain a mock to the real object's shape:
Mock(spec=RealClass): accessing attributes that don't exist on the class raisesAttributeError, andisinstance(m, RealClass)is True. Signatures are not checked.spec_set=: also forbids setting non-existent attributes.create_autospec(obj)orpatch(..., autospec=True): recursively specs attributes and checks call signatures, for functions and methods. Calling with wrong arguments raisesTypeError, just like the real thing. Methods are specced withoutself, and the result tracks the real signature.seal(mock)stops a mock from creating new child attributes after you've configured it.
Limitations: autospec only sees attributes that exist on the class, so instance attributes set in __init__ are missing unless they are declared as class-level annotations or defaults. Autospeccing large objects costs some time, and properties or descriptors may be triggered during introspection.
Rule of thumb: default to autospec=True when patching your own code's functions and classes.
Example
from unittest.mock import Mock, create_autospec, patch, seal
class Mailer:
def send(self, to: str, subject: str) -> bool:
return True
Mock().sennd("x") # plain Mock: typo silently accepted
strict = create_autospec(Mailer, instance=True)
strict.send("a@x.io", subject="hi") # matches the real signature
for bad in (lambda: strict.sennd("x"), lambda: strict.send("a@x.io")):
try:
bad()
except (AttributeError, TypeError) as e:
print(type(e).__name__, str(e)[:45])
# AttributeError Mock object has no attribute 'sennd'
# TypeError missing a required argument: 'subject'
m = Mock()
m.client.get.return_value = 1
seal(m)
try:
m.client.post
except AttributeError as e:
print("sealed:", e) # sealed: mock.client.postRemember
- spec= restricts attributes; spec_set also restricts assignment
- autospec / create_autospec also enforces call signatures
- seal() freezes a configured mock
Watch out for
- Relying on autospec for instance attributes created in __init__; they don't exist on the class, so the spec rejects them.