Builder pattern for mocking complex objects in Python

Use a fluent Builder to construct realistic mock objects with defaults, enabling readable test data setup.

Easy Python 3.9+ Aug 9, 2026 System design patterns 15 views 0 copies

Python code

45 lines
Python 3.9+
class 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

stdout
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

  1. Use a `copy()` method on the builder to create immutable snapshots for each test.
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from System design patterns

Related tutorials and quizzes for this topic.