Test data builders
Skill Amey-Thakur/AI-SKILLS/skills/testing/test-data-builders
Construct test objects through builders and factories with valid defaults so each test states only the fields it cares about. Use when shared fixture files couple unrelated tests and drift as the schema grows.From its SKILL.md
npx -y skills add Amey-Thakur/AI-SKILLS --skill test-data-buildersAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 23 days oldThe repository was created 23 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
2.8 KB, 607 tokens by cl100k_base, as published. Nobody here has run it
Test data builders
A shared fixture file starts as a convenience and ends as a liability. Twenty
tests read the same users.json, so nobody dares edit it, and each new case
bolts on a field that unrelated tests now silently depend on. A builder
constructs exactly the object a test needs at the point of use, filling every
required field with a sensible default and making only the salient one explicit.
Method
- Give every builder valid defaults so tests set only what matters.
aUser().build()returns a complete, persistable user with plausible values for every required field. A test about suspension writesaUser().withStatus(SUSPENDED).build()and the reader sees status is the whole point. - Put the field under test on the line, hide the rest. For an expired-card
case,
.withExpiry(yesterday)belongs in the test body; the name, address, and SKU do not. Required-but-irrelevant data lives in defaults so the test reads as its own intent. - Use a factory library for persistence and unique fields. factory_bot,
Factory Boy, or a factory function handle database insertion and sequences
(
sequence(:email) { "user#{n}@x.test" }) so no two rows collide. Prefer an in-memorybuildovercreateunless the test truly needs the database. - Compose builders for object graphs. Nest them,
anOrder().withCustomer(aCustomer().inTier(GOLD)).withLines(3), so a whole related structure is one readable expression instead of hand-wired foreign keys. - Keep randomness deterministic or absent. Fill fields a test never asserts on with Faker, but seed the generator per run so a failure replays. Values that change every run turn a real bug into a flake.
- Name reusable shapes as traits, then delete the fixtures. Express common
states as methods (
aUser().admin(), anexpiredtrait) so a change to what "admin" means lands in one place, and remove the JSON fixtures once builders cover the domain.
Litmus tests
- Can a reader tell what a test cares about from its builder calls alone?
- Does adding a required field to the model touch one builder default, not every test?
- Is any test asserting on a value it never set explicitly?
Boundaries
Builders construct inputs for unit and integration tests, not the assertions themselves. A tiny value object is often clearer built inline with its constructor. A large volume of realistic data for a running environment is a different job: defer to seed-data-management. A specific captured payload from production stays an explicit file rather than being forced through a builder.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.