Produce test data that is deterministic, isolated per test, and states its intent in the test that uses it.
Scanned 9/10/2026
Install to Claude Code
npx -y skills add snoodleboot-io/prompticorn --skill minimal --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Minimal?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/snoodleboot-io-minimal-6d32c865)More formats (shields.io, HTML) on the badges page.
# Test Data Strategies (Minimal)
## Purpose
Produce test data that is deterministic, isolated per test, and states its intent in the test that uses it.
## Core Techniques
### 1. Choose the Right Construct
| Construct | Shape | Use when |
|---|---|---|
| Fixture | One prepared value, reused | Immutable reference data, expensive setup |
| Factory | Function returning a fresh object with defaults | The common case — most objects |
| Builder | Fluent chain of overrides | Objects with many fields where a few matter |
Default to factories. Fixtures are for things nobody mutates; builders earn their keep once an object has ten-plus fields.
### 2. Never Share Mutable Fixtures
```python
# ❌ Session-scoped and mutable: test order now decides the result
@pytest.fixture(scope="session")
def user():
return User(name="Alice", credits=100)
def test_spend(user):
user.credits -= 50 # mutates the shared object
assert user.credits == 50 # passes, and poisons every later test
```
Function scope is the default for a reason. If a fixture must be session-scoped for cost, make it frozen or hand each test a copy.
### 3. Build Fresh Objects With Explicit Overrides
```python
def make_order(**overrides) -> Order:
defaults = dict(
id=next(_ids),
customer_id=next(_ids),
total=Decimal("100.00"),
status="pending",
placed_at=datetime(2026, 1, 1, tzinfo=UTC),
)
return Order(**defaults | overrides)
def test_refund_rejected_for_shipped_order():
order = make_order(status="shipped") # only the field under test is named
with pytest.raises(RefundNotAllowed):
refund(order)
```
The override *is* the documentation: a reader sees instantly that `status` is what this test is about.
### 4. Make Generated Data Deterministic
```python
fake = Faker()
Faker.seed(20260101) # same names, addresses, emails on every run
```
Unseeded generators turn a data-dependent bug into a flake that appears once a month. Seed everything; if you want variety, vary the seed deliberately and log it.
### 5. Keep Uniqueness Explicit
Collisions on unique columns are a classic order-dependent failure. Use a per-run counter rather than random values, which collide by birthday paradox sooner than people expect:
```python
_ids = itertools.count(1)
email = f"user-{next(_ids)}@example.test"
```
### 6. Treat Production Data as Radioactive
A production dump in a test database is a breach waiting for a misconfigured bucket. Naive masking does not help: replacing names while keeping birth date, ZIP code, and sex leaves records re-identifiable, and free-text fields, JSON blobs, and logs carry PII that a column-level masker never sees. Generate synthetic data by default. If real data is unavoidable, restrict to a subset, transform irreversibly, keep it inside the same trust boundary as production, and give it an expiry.
## Warning Signs
- `scope="session"` on a fixture whose object gets mutated
- Tests that pass alone and fail in the suite, or vice versa
- Unseeded `Faker`, `random`, or UUIDs in setup
- One `conftest.py` mega-fixture that most tests use and none needs entirely
- Assertions on values the test never set — inherited from fixture defaults
- Real customer names or emails visible in test data or CI logs
- Test data loaded from a checked-in SQL dump nobody can regenerate
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!