filipebraida/adonisjs-starter-kit/testing
Claude Code and agent skill by filipebraida/adonisjs-starter-kit
- community
Checking that the SKILL.md file is still in the repository.
Japa test patterns for an AdonisJS app. Functional specs hit real Postgres wrapped in per-test transactions; unit specs stub deps with sinon. Fakes are mandatory for anything that reaches out (mail, drive, emitter, transmit). Trigger on: "write test", "add spec", "cover with tests", "how do we test X", "mock", "fake", "stub".
Install
npx vskill@latest install filipebraida/adonisjs-starter-kit/testingSKILL.md
Testing
Two suites: unit for action/service internals with dependencies stubbed; functional for HTTP endpoints hit against a real Postgres wrapped in a per-test transaction. Every spec matches the shape of the nearest existing spec — same imports, same setup/teardown order, same helpers. Anything that reaches out (mail, drive, emitter, transmit, third-party HTTP) must be faked; a global mail fake in tests/bootstrap.ts is a safety net so a forgotten setup doesn't send a real email.
Mandatory fakes by dependency
Every side-effect dependency needs an explicit fake so no test reaches real infra:
| Dependency | How to fake | Scope | |
|---|---|---|---|
mail.fake() | Global in tests/bootstrap.ts + per-test when asserting | ||
| Drive | drive.fake(env.get('DRIVE_DISK') ?? 'fs') | Per-group setup + teardown | |
| Emitter | emitter.fake(['event:name']) → EventsBuffer | Per-test opt-in | |
| Transmit (SSE) | transport: null in config/transmit.ts (default) | Global |
emitter.fake takes an array of event names — a bare string is a TypeScript error:
Rules
- Location:
app/<mod>/tests/{unit,functional}/*.spec.ts. - Group setup: every group opens with
group.each.setup(() => testUtils.db().wrapInGlobalTransaction())when the code under test touches DB (nearly always). - RBAC helpers: functional specs involving roles/permissions call
group.each.setup(() => ensureBaseRoles()); assign per user withwithRole(user, ROLES.X)/withPermissions(user, [...]). - Emitter fake: to isolate an endpoint from its listeners,
const fake = emitter.fake(['event:name'])in setup +emitter.restore()in teardown. Assert withfake.assertEmitted('event:name')/fake.assertNoneEmitted(). - Mail fake:
const mails = mail.fake()in the test, thenmails.assertSent(Notification, (msg) => ...)ormails.assertNoneSent(). - Drive fake:
drive.fake(...)in group setup,drive.restore(...)in teardown. Assert withdisk.assertExists(path),disk.assertMissing(path). - Sinon for stubbing services, drivers, and single functions in unit tests:
sinon.stub<[Arg], Promise<Ret>>().resolves(...). Alwaysgroup.each.teardown(() => sinon.restore()). - Factories over fixtures — use
UserFactory.create()/UserFactory.merge({...}).create()instead of hand-building rows. - CSRF: POST/PUT/DELETE must include
.withCsrfToken()— shield middleware is enforced. - Auth: use
.loginAs(user)on the request builder — sets the session cookie for that user's guard. - JSON vs HTML: chain
.accept('json')when asserting a JSON response body,.withInertia()for Inertia flows so the exception handler treats the request as UI. - DB assertions:
db.assertHas('table', { column: value }),db.assertMissing('table', { ... })— plugin from@adonisjs/lucid/plugins/db.
Repo refs
- Functional spec (per-test transaction via
client):app/users/tests/functional/users_endpoint.spec.ts. - Unit spec (sinon, action in isolation):
app/users/tests/unit/create_user.spec.ts. - RBAC helper:
tests/helpers/rbac.ts. Bootstrap fakes:tests/bootstrap.ts.
Doc refs
- Japa runner — https://japa.dev/docs
- AdonisJS testing intro — https://docs.adonisjs.com/guides/testing/introduction
- Fake mail / drive — https://docs.adonisjs.com/guides/digging-deeper/mail#fakes
Workflow
Functional endpoint spec
Cover: unauth (302 → /login), forbidden (via .withInertia() + db.assertHas — see below), validation error (422 when JSON), happy path.
Testing authorization in an Inertia app
The exception handler for E_AUTHORIZATION_FAILURE branches on Accept:
- JSON: returns 403.
- HTML / Inertia (what the real client sends): flashes +
redirect().back()→ 302.
.assertStatus(403) on .accept('json') exercises a code path the UI never triggers. Assert the effect — the state that would change if the block failed:
Rules of thumb:
- Ownership tests use
.withInertia()+db.assertMissing/db.assertHasto check the effect. - Only assert status codes when the client cares about the exact number (422,
Location: /login).
Unit spec for an action
To stub an injected dep (like an auth guard), fake the ctx shape with as unknown as:
Faking side effects
- Mail — asserted per-message:
``ts const mails = mail.fake() await someAction() mails.assertSent(WelcomeNotification, (msg) => msg.to === user.email) ``
- Emitter — verify emit without running the listener:
``ts const fake = emitter.fake(['user:registered']) // ... trigger endpoint fake.assertEmitted('user:registered') ``
- Emitter with listener — for end-to-end listener specs, do not fake the emitter.
mail.fake()first (so the listener's mail send is captured, not sent), thenawait emitter.emit('user:registered', payload), then assert the effect (DB row created, mail sent, etc).
Anti-patterns
- ❌ Skipping
wrapInGlobalTransaction()— rows leak between tests, ordering matters, flakiness appears. - ❌ Hand-built fixture rows when a factory exists — inconsistent baseline data.
- ❌ Real network / mail / drive calls in tests — flaky, slow, sometimes sends real emails.
- ❌ POST/PUT/DELETE without
.withCsrfToken()— will 403. - ❌ Asserting redirect location on a JSON call — chain
.accept('json')instead. - ❌ Asserting the exception handler's 302 as if it were the policy's return — assert the resource state (
assertHas/assertMissing), not the redirect. - ❌ Sharing state between tests via module-level variables — transaction rollback doesn't clean JS state.
- ❌ Testing controllers directly (instantiate + call handler) — go through
clientso middleware, validators and policies actually run. - ❌ Faking the emitter and expecting the listener to run — the fake swallows the event.
Related skills
[[actions-events]] · [[authorization]] · [[crud]]
Security
- Security scanPassed, 90 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingpackages/skills/testing/SKILL.md
Popularity
- Repository stars
- 94
- Repository forks
- 12
- 7-day trend score
- 23
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/filipebraida/adonisjs-starter-kit/testing)