Builder pattern for mocking complex objects in Python
Use a fluent Builder to construct realistic mock objects with defaults, enabling readable test data setup.
Python code
45 linesclass User:
def __init__(self):
self.name = "default"
self.age = 0
self.email = "unknown@example.com"
self.address = "unknown"
def __repr__(self):
return f"User(name={self.name!r}, age={self.age}, email={self.email!r}, address={self.address!r})"
class UserBuilder:
def __init__(self):
self.user = User()
def with_name(self, name):
self.user.name = name
return self
def with_age(self, age):
self.user.age = age
return self
def with_email(self, email):
self.user.email = email
return self
def with_address(self, address):
self.user.address = address
return self
def build(self):
return self.user
if __name__ == "__main__":
mock_user = (
UserBuilder()
.with_name("Alice")
.with_age(30)
.with_email("alice@example.com")
.with_address("123 Main St")
.build()
)
print(mock_user)
Output
User(name='Alice', age=30, email='alice@example.com', address='123 Main St')
How it works
The UserBuilder keeps a User instance and returns self from each setter, enabling chained calls. This fluent interface makes test data setup readable and hides the object's constructor complexity. Defaults in User.__init__ ensure only overridden fields change, so tests remain concise. The build() method returns the fully configured object, which can be passed directly to code under test.
Common mistakes
- Forgetting to call `build()` and passing the builder instead of the object.
- Returning a new object from setter methods breaks chaining; always return `self`.
- Mutating the same builder instance for multiple tests without resetting defaults.
Variations
- Use a `copy()` method on the builder to create immutable snapshots for each test.
- Implement `__getattr__` for a generic builder that sets any attribute dynamically.
Real-world use cases
- Creating consistent mock users in unit tests without repeating setup code.
- Building complex input payloads for integration tests (e.g., API requests).
- Providing fixtures for data‑heavy services such as order or account objects.
Sponsored
More from System design patterns
- Build a BFF (Backend for Frontend) Mock Aggregator in Python medium
- Circuit Breaker Pattern in Python: Closed, Open, and Half-Open States medium
- Create a Data Helper Class in Python easy
- Domain Driven Design Aggregate Root Example in Python medium
- Facade Pattern in Python with Mock Simplification medium
- How to Aggregate Mock API Routes by Method in Python easy
Keep learning
Related tutorials and quizzes for this topic.