principleMajorpending
Principle: Test the interface, not the implementation
Viewed 0 times
test interfaceblack box testingbehavior testingimplementation couplingrefactor safe
Problem
Tests break every time implementation details change, even when the behavior is correct. Tests are tightly coupled to internal structure.
Solution
Write tests that verify behavior through the public interface:
Guidelines:
# BAD: Testing implementation details
def test_user_service():
service = UserService()
service.create_user('alice')
# Testing internal state
assert service._cache['alice'] is not None
assert service._db.query_count == 1
assert service._validator.was_called
# GOOD: Testing behavior through public API
def test_create_user_returns_user():
service = UserService()
user = service.create_user('alice')
assert user.name == 'alice'
def test_create_user_is_retrievable():
service = UserService()
service.create_user('alice')
user = service.get_user('alice')
assert user.name == 'alice'
def test_duplicate_user_raises():
service = UserService()
service.create_user('alice')
with pytest.raises(DuplicateUserError):
service.create_user('alice')Guidelines:
- Test inputs and outputs, not internals
- Test what it does, not how it does it
- If you need to refactor to make code testable, that's a design smell
- Mock at boundaries (network, filesystem), not between your own classes
Why
Implementation-coupled tests must be rewritten on every refactor, providing no safety net for the very changes they should protect against.
Context
Any codebase with unit tests that frequently break during refactoring
Revisions (0)
No revisions yet.