Backend Test Fixtures
SkillFiles & storageShared test fixtures for backend specs — port mock factories, domain builders, and shared IDs in a per-module testing/ folder. Use when a module's *.use-case.spec.ts files repeat the same mock-repository literal, inline entity construction, or hardcoded UUIDs across files.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Backend Test Fixtures skill
What this skill tells your AI
The instructions your AI receives, as published by ayunis-core/ayunis-core in .claude/skills/backend-test-fixtures/SKILL.md and read by ahel’s review.
Most repetition in backend unit specs is setup, not assertions: the same
port mock object, the same new Entity({...}) literals, and the same hardcoded
UUIDs copy-pasted into every *.spec.ts. Extract that setup into a per-module
fixtures file so a port-signature change is a one-line edit instead of a
nine-file sweep.
Extract setup. Keep assertions explicit. Per-test expect(...).toEqual(...)
bodies document the contract — never hide them behind helpers.
When to use
- A module has ≥3 specs that each re-declare the same
jest.Mocked<XRepository>literal. - Specs build the same domain entity inline (
new UserCreditLimit({...})) repeatedly. - The same UUID literals (
'1111…','2222…') appear across multiple specs.
When NOT to use: a single spec, or a one-off mock used in one place. Don't build infrastructure for one caller.
The hard constraint: keep jest out of the production build
A fixtures file uses the jest global, so it must never reach the production
tsc build. tsconfig.build.json only excludes **/*spec.ts, so the file
cannot be named *.fixtures.ts at an arbitrary path.
Put it in a testing/ folder, which is excluded from both the build and
coverage:
tsconfig.build.json→excludeincludes"**/testing/**"package.jsonjestcollectCoverageFrom→ includes"!**/testing/**"
Both exclusions already exist (added with the credit-limits fixtures). Place new
fixtures under <module>/application/testing/ and they are covered. Import them
only from *.spec.ts files.
The pattern (canonical example)
See src/iam/credit-limits/application/testing/credit-limit.fixtures.ts and any
of src/iam/credit-limits/application/use-cases/*/*.use-case.spec.ts.
A fixtures file exports three things:
// 1. Shared identifiers — one source of truth for test IDs.
export const TEST_ORG_ID = '11111111-1111-1111-1111-111111111111' as UUID;
export const TEST_USER_ID = '22222222-2222-2222-2222-222222222222' as UUID;
export const TEST_TEAM_ID = '33333333-3333-3333-3333-333333333333' as UUID;
// 2. Domain builders — sensible defaults, override only what the test asserts.
// One builder per entity subclass, so there is no union to juggle.
export function aUserCreditLimit(
overrides: { orgId?: UUID; userId?: UUID; monthlyCredits?: number } = {},
): UserCreditLimit {
const { orgId = TEST_ORG_ID, userId = TEST_USER_ID, monthlyCredits = 5000 } = overrides;
return new UserCreditLimit({ orgId, userId, monthlyCredits });
}
// 3. A port mock factory — defaults model the "empty" state. No `as` cast needed:
// the object literal already satisfies jest.Mocked<T>.
export function createMockCreditLimitRepository(): jest.Mocked<CreditLimitRepository> {
return {
save: jest.fn().mockImplementation((limit) => Promise.resolve(limit)),
findUserLimits: jest.fn().mockResolvedValue([]),
findByUserId: jest.fn().mockResolvedValue(null),
// …every port method, with a sensible default…
};
}
In a spec, setup collapses to:
const orgId = TEST_ORG_ID;
const repository = createMockCreditLimitRepository();
const existing = aUserCreditLimit({ monthlyCredits: 1000 });
repository.findByUserId.mockResolvedValue(existing); // override per test
Rules
- Defaults model the empty/happy path. Mock finders resolve to
null/[],saveechoes its argument, deletes resolve. Tests override per case. import typefor type-only imports (the port is only used injest.Mocked<T>), and noas jest.Mocked<T>cast on the literal — both trip ESLint otherwise.- One builder per entity subclass (
aUserCreditLimit/aTeamCreditLimit) rather than a single builder that branches on scope. - Keep the dominant spec style (
Test.createTestingModule) — fixtures replace the mock setup, not the testing harness.
Validate
pnpm jest src/<module> # specs still green
pnpm exec tsc -p tsconfig.build.json --noEmit # testing/ NOT compiled
pnpm exec eslint --max-warnings=0 <changed files> # mirrors pre-commit
Signals
- GitHub stars
- 33
- Forks
- 3
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
backend-test-fixtures- Source
- github.com/ayunis-core/ayunis-core