⚒️ Object Mother Pattern
The Object Mother Pattern is a Python 🐍 package that simplifies and standardizes the creation of test 🧪 data.
Instead of hand-writing random values, invalid values, range boundaries, dates, identifiers, URLs, credit cards, and
other fixtures in every test module, you use reusable object mother classes with consistent validation behavior.
Table of Contents
- 📥 Installation
- 📚 Documentation
- ⚡ Quick Start
- 🧩 Core Ideas
- 🤔 Why Object Mothers?
- 📃 Available Mothers
- 🧪 Testing Patterns
- 🎄 Real-Life Case: Christmas Detector Service
- 🧑🔧 Creating Your Own Object Mother
- 🤝 Contributing
- 🔑 License
📥 Installation
You can install Object Mother Pattern using pip:
pip install object-mother-pattern
You can install the companion AI-agent skill from skills.sh with Vercel's skills CLI:
npx skills add adriamontoto/object-mother-pattern
Review the skill source in skills/object-mother-pattern before installing it in
sensitive environments.
📚 Documentation
The root README is the entry point. Deeper guides live in this repository and are linked here:
docs/README.md: Documentation hub.docs/usage/README.md: Core usage, imports, validation, and custom mothers.docs/catalog/README.md: Feature map by mother category.- Catalog details:
primitives,dates,identifiers,internet,money, andpeople/text. docs/testing/README.md: Testing patterns for valid, invalid, boundary, and deterministic data.AI Skill: Installable skill package that teaches AI agents how to use Object Mother Pattern.
This project's DeepWiki documentation is also available for generated repository navigation.
⚡ Quick Start
Use mothers directly in tests when you need valid values:
from object_mother_pattern import (
BooleanMother,
DateMother,
FloatMother,
IntegerMother,
StringMother,
)
from object_mother_pattern.mothers.identifiers.uuid import UuidV4Mother
integer = IntegerMother.create(min=-4, max=15)
amount = FloatMother.create(min=0, max=100, decimals=2)
enabled = BooleanMother.create()
username = StringMother.lowercase(min_length=8, max_length=16)
date = DateMother.create()
uuid = UuidV4Mother.create()
Pass an explicit value when a test needs a known valid value and you still want the mother to validate it:
from object_mother_pattern import IntegerMother
assert IntegerMother.create(value=7) == 7
Generate values outside an accepted range when testing validation failures:
from datetime import date
from object_mother_pattern import DateMother
value = DateMother.out_of_range(
start_date=date(year=2025, month=1, day=1),
end_date=date(year=2025, month=1, day=31),
)
🧩 Core Ideas
Most mothers follow the same pattern:
create(value=...)validates and returns an explicit value.create(...)withoutvaluegenerates a valid random value.- Dedicated helpers such as
empty(),of_length(),positive(),odd(), orout_of_range()express common test shapes. invalid_type()generates a value with the wrong type for negative-path tests.- Collection mothers can compose other mothers with callables such as
item_mother,key_mother, andvalue_mother.
from object_mother_pattern.models import DictMother, ListMother
from object_mother_pattern import IntegerMother, StringMother
values = ListMother.of_length(length=3, item_mother=IntegerMother.positive)
payload = DictMother.of_length(
length=2,
key_mother=lambda: StringMother.lowercase(min_length=4, max_length=8),
value_mother=IntegerMother.create,
)
🤔 Why Object Mothers?
Object mothers help tests say what kind of value they need instead of repeating how that value is built. A hardcoded
literal such as 'john@example.com', '550e8400-e29b-41d4-a716-446655440000', or 42 can be perfectly valid, but when
every test invents its own literals the setup becomes duplicated, inconsistent, and harder to update when a validation
rule changes.
Use object mothers when the exact value does not matter and the test only needs a value that satisfies a shape:
from object_mother_pattern import IntegerMother
from object_mother_pattern.mothers.internet import EmailAddressMother
email = EmailAddressMother.create()
quantity = IntegerMother.positive()
assert '@' in email
assert quantity > 0
This keeps the test focused on the behavior under test. The mother owns the details of valid email generation, positive integer generation, invalid types, ranges, and special formats. If those rules evolve, tests that only need "a valid email" or "a positive integer" do not need to be rewritten.
Hardcoded values are still the best choice when the value is the point of the test:
- Use fixed literals for exact serialization, snapshots, URLs, JSON, SQL, error messages, and UI copy.
- Use explicit boundary values for deliberate limits such as minimum length, maximum length, zero, one, empty strings, first date, last date, or a known invalid format.
- Use a known literal when reproducing a specific bug.
- Use
create(value=...)when you want an explicit value but still want the mother to validate that it belongs to the expected shape.
from object_mother_pattern import IntegerMother, StringMother
assert StringMother.create(value='invoice-paid') == 'invoice-paid'
assert IntegerMother.create(value=10) == 10
The practical rule is simple: generate values for invariant-based tests, hardcode values for exact examples and intentional limits.
📃 Available Mothers
The package includes mothers for:
| Category | Examples |
|---|---|
| Models | BaseMother, EnumerationMother, ListMother, DictMother |
| Primitives | BooleanMother, BytesMother, FloatMother, IntegerMother, StringMother |
| Dates | DateMother, DatetimeMother, StringDateMother, StringDatetimeMother, timezone mothers |
| Identifiers | UUID mothers, Spanish identifiers, world/country code identifiers |
| Internet | URLs, domains, hosts, ports, IP addresses, networks, MAC addresses, email addresses, user agents |
| Money | IBANs, credit cards by brand, Bitcoin wallet values |
| People | Full names, usernames, passwords |
| Extra | Text snippets |
See docs/catalog/README.md for a deeper feature map and import guidance.
🧪 Testing Patterns
Use explicit values when an assertion depends on exact output:
from object_mother_pattern import StringMother
assert StringMother.create(value='known-value') == 'known-value'
Use generated values when the test only cares about invariants:
from object_mother_pattern import IntegerMother
value = IntegerMother.positive()
assert value > 0
Use invalid helpers for negative-path coverage:
from pytest import raises
from object_mother_pattern import IntegerMother
with raises(TypeError):
IntegerMother.create(value=IntegerMother.invalid_type())
More guidance is available in docs/testing/README.md.
🎄 Real-Life Case: Christmas Detector Service
This service checks whether a date falls within a Christmas holiday range. Tests use
DateMother
to create dates inside and outside the accepted range without duplicating date-generation logic.
from datetime import date
from object_mother_pattern import DateMother
class ChristmasDetectorService:
def __init__(self) -> None:
self._christmas_start = date(year=2024, month=12, day=24)
self._christmas_end = date(year=2025, month=1, day=6)
def is_christmas(self, today: date) -> bool:
return self._christmas_start <= today <= self._christmas_end
christmas_detector_service = ChristmasDetectorService()
def test_christmas_detector_is_christmas() -> None:
today = DateMother.create(
start_date=date(year=2024, month=12, day=25),
end_date=date(year=2025, month=1, day=6),
)
assert christmas_detector_service.is_christmas(today=today)
def test_christmas_detector_is_not_christmas() -> None:
today = DateMother.out_of_range(
start_date=date(year=2024, month=12, day=24),
end_date=date(year=2025, month=1, day=6),
)
assert christmas_detector_service.is_christmas(today=today) is False
🧑🔧 Creating Your Own Object Mother
You can extend the functionality of this library by subclassing the
BaseMother
class. A custom mother should validate explicit values and generate valid values when no value is provided.
from random import randint
from object_mother_pattern.models import BaseMother
class PositiveIntegerMother(BaseMother[int]):
@classmethod
def create(cls, *, value: int | None = None) -> int:
if value is not None:
if type(value) is not int:
raise TypeError('PositiveIntegerMother value must be an integer.')
if value <= 0:
raise ValueError('PositiveIntegerMother value must be positive.')
return value
return randint(a=1, b=100)
More detail is available in docs/usage/README.md.
🤝 Contributing
We love community help! Before you open an issue or pull request, please read:
Thank you for helping make ⚒️ Object Mother Pattern package awesome! 🌟
🔑 License
This project is licensed under the terms of the MIT license.
Metadata
Release files for object-mother-pattern 3.16.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| object_mother_pattern-3.16.0.tar.gz | 95.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| object_mother_pattern-3.16.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 252.0 kB
Release files / object_mother_pattern-3.16.0.tar.gz
| Download URL | object_mother_pattern-3.16.0.tar.gz |
|---|---|
| Size | 95.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3fb98f1554a03a055e4da8d282ee123fb456f29d97bb8033d8d6542f111d4750
|
|
BLAKE2b-256 checksum How to use checksums |
7cd127e3769e3660eb0aa853ed5675912ef57ac6be98113c08a5eb650bae2cd5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 18, 2026.
Transparency logRelease files / object_mother_pattern-3.16.0-py3-none-any.whl
| Download URL | object_mother_pattern-3.16.0-py3-none-any.whl |
|---|---|
| Size | 156.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
3f376afe6ed3591d5337e24699732926f01f295c9b62831708e464653f0ccd05
|
|
BLAKE2b-256 checksum How to use checksums |
b138c90eda1e0f141a70f4410b4a20f33f27897ae2a6d6373b5bf1be3a196652
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 18, 2026.
Transparency log