Test-Fixture Realism
Type: Open-source — client-agnostic methodology, no project-specific detail.
Created by akbarsha — https://github.com/iamakbarsha1
Distilled from a recurring cluster of test failures where the fixtures didn't
match data reality or the assertion could pass without the code working.
Licence: Released under CC BY 4.0 — share and adapt for any purpose with
credit. Full text: LICENSE at the repository root.
Feedback & Support: If a rule here proves wrong or needs sharpening,
open an issue on the repository or contact the author at the profile link
above. If the problem is the agent not following a rule below rather than
the rule itself, that's an execution failure — acknowledge and correct it.
The core rule
A test proves something only if two things hold: its fixtures resemble the
data the code meets in production, and its assertions cannot pass unless the
code actually worked. Fabricated fixtures that skip referential integrity,
aggregate assertions over a shared store, unseeded randomness, and mocks that
echo the input all produce green tests that verify nothing. Build fixtures that
are real enough to exercise the code, and assertions specific enough to fail
when it breaks.
Checks
- Assert on the rows YOU created, not aggregates. Against a shared DB or
store, other data and parallel tests pollute any
count(*) or "all rows"
assertion. Tag the rows your test inserts (a marker, a returned id) and
assert on exactly those. (A test asserted a total row count; another test's
seed data made it flap between pass and fail.)
- Fixtures need real referential integrity. A foreign key must point to a
parent row that actually exists — create the parent, use its RETURNED id.
A made-up id either errors or silently matches nothing, and the test then
exercises an empty join. (A fixture used a hardcoded parent id; the join
returned zero rows and the test asserted on emptiness without noticing.)
- Kill nondeterminism: seed randomness, pin the clock. Random fixture
values and time-derived keys make tests flaky and can collide across runs.
Seed the RNG and pass a fixed time in (see the
inject-ambient-inputs
skill), so the same inputs produce the same fixtures every run. (Fixtures
keyed on the current time occasionally collided, producing an
order-dependent failure.)
- A guard's fixture must fail closed. When testing a validation/guard, the
fixture that VIOLATES it must actually trip the failure path — an omitted or
malformed field should make the assertion fail, not pass by default. This is
the fixture side of
prove-the-test-can-fail. (A "reject when field
missing" test used a fixture that still had the field, so it never exercised
the rejection.)
- Watch mocks that load the real module or echo the input. Automock and
partial mocks can pull in the real module and run its side effects; and a
test whose expected value is computed from the same call it's testing (a
search that echoes its own query) passes trivially. Assert against an
INDEPENDENT expectation, and confirm the mock isn't executing real code.
(Illustrative: A partial mock stubbed one method on a client but left another untouched;
the untouched method fell through to the real implementation and made a
live call during the test run.)
- Secret-shaped fixtures must not ship the flagged bytes. Code that handles
secrets needs fixtures shaped like real secrets — but a scanner-recognized
token written as a contiguous literal trips upstream secret scanners (push
protection, pre-commit hooks) and blocks the push, even though the value is
fake. Build the token at runtime by splitting the provider prefix across
adjacent literals (
"xox""b-…"), so the file's bytes never contain the
flagged prefix while the runtime value keeps the real shape; grep the source
for the contiguous prefix before committing. (A secret scrubber's test
fixtures held crafted provider-prefixed tokens as plain literals; GitHub push
protection matched its own secret signatures and rejected the push until the
prefixes were split across adjacent strings.)
Pre-flight check — before you trust a fixture-backed test
If any box is unchecked, the fixture doesn't mirror reality — go make it real
before you trust the green.
1---2name: test-fixture-realism3description: Use when writing tests against a shared store or with fabricated fixtures — a test only proves something if its fixtures mirror data reality and its assertions can't pass trivially. Covers asserting on rows you created (not aggregate counts), real referential integrity, seeded randomness and pinned time, fail-closed guard fixtures, and mocks that quietly load the real module. Triggers on "test fixtures", "seed data", "test DB", "factory", "mock returns", "flaky test", "assert count", "foreign key in test", "automock", and building any non-trivial test's setup.4---56# Test-Fixture Realism78**Type:** Open-source — client-agnostic methodology, no project-specific detail.910**Created by akbarsha — https://github.com/iamakbarsha1**1112Distilled from a recurring cluster of test failures where the fixtures didn't13match data reality or the assertion could pass without the code working.1415**Licence:** Released under CC BY 4.0 — share and adapt for any purpose with16credit. Full text: `LICENSE` at the repository root.1718**Feedback & Support:** If a rule here proves wrong or needs sharpening,19open an issue on the repository or contact the author at the profile link20above. If the problem is the agent not following a rule below rather than21the rule itself, that's an execution failure — acknowledge and correct it.2223## The core rule2425A test proves something only if two things hold: its fixtures resemble the26data the code meets in production, and its assertions cannot pass unless the27code actually worked. Fabricated fixtures that skip referential integrity,28aggregate assertions over a shared store, unseeded randomness, and mocks that29echo the input all produce green tests that verify nothing. Build fixtures that30are real enough to exercise the code, and assertions specific enough to fail31when it breaks.3233## Checks3435- **Assert on the rows YOU created, not aggregates.** Against a shared DB or36 store, other data and parallel tests pollute any `count(*)` or "all rows"37 assertion. Tag the rows your test inserts (a marker, a returned id) and38 assert on exactly those. *(A test asserted a total row count; another test's39 seed data made it flap between pass and fail.)*40- **Fixtures need real referential integrity.** A foreign key must point to a41 parent row that actually exists — create the parent, use its RETURNED id.42 A made-up id either errors or silently matches nothing, and the test then43 exercises an empty join. *(A fixture used a hardcoded parent id; the join44 returned zero rows and the test asserted on emptiness without noticing.)*45- **Kill nondeterminism: seed randomness, pin the clock.** Random fixture46 values and time-derived keys make tests flaky and can collide across runs.47 Seed the RNG and pass a fixed time in (see the `inject-ambient-inputs`48 skill), so the same inputs produce the same fixtures every run. *(Fixtures49 keyed on the current time occasionally collided, producing an50 order-dependent failure.)*51- **A guard's fixture must fail closed.** When testing a validation/guard, the52 fixture that VIOLATES it must actually trip the failure path — an omitted or53 malformed field should make the assertion fail, not pass by default. This is54 the fixture side of `prove-the-test-can-fail`. *(A "reject when field55 missing" test used a fixture that still had the field, so it never exercised56 the rejection.)*57- **Watch mocks that load the real module or echo the input.** Automock and58 partial mocks can pull in the real module and run its side effects; and a59 test whose expected value is computed from the same call it's testing (a60 search that echoes its own query) passes trivially. Assert against an61 INDEPENDENT expectation, and confirm the mock isn't executing real code.62 *(Illustrative: A partial mock stubbed one method on a client but left another untouched;63 the untouched method fell through to the real implementation and made a64 live call during the test run.)*65- **Secret-shaped fixtures must not ship the flagged bytes.** Code that handles66 secrets needs fixtures shaped like real secrets — but a scanner-recognized67 token written as a contiguous literal trips upstream secret scanners (push68 protection, pre-commit hooks) and blocks the push, even though the value is69 fake. Build the token at runtime by splitting the provider prefix across70 adjacent literals (`"xox""b-…"`), so the file's bytes never contain the71 flagged prefix while the runtime value keeps the real shape; grep the source72 for the contiguous prefix before committing. *(A secret scrubber's test73 fixtures held crafted provider-prefixed tokens as plain literals; GitHub push74 protection matched its own secret signatures and rejected the push until the75 prefixes were split across adjacent strings.)*7677## Pre-flight check — before you trust a fixture-backed test7879- [ ] Assertions target the specific rows/records the test created, not80 aggregates over a shared store.81- [ ] Every foreign key / reference points to a real parent the test created,82 using its returned id.83- [ ] Randomness is seeded and time is pinned, so fixtures are deterministic.84- [ ] Any guard test's violating fixture actually trips the failure path.85- [ ] Mocks don't secretly load the real module, and no assertion is satisfied86 by echoing the input.87- [ ] No secret-shaped fixture commits a scanner-recognized token as a88 contiguous literal — the flagged prefix is split and rebuilt at runtime89 (verified by grep).9091If any box is unchecked, the fixture doesn't mirror reality — go make it real92before you trust the green.