Testing
Mandu ships its own batteries-included test runner — unit, integration, E2E, coverage, watch — all driven by a single `mandu test` command.
On this page
Testing
Mandu ships its own batteries-included test runner. No Vitest. No Jest. No
Playwright setup (until you want E2E). Just mandu test.
# Everything, sequential: unit → integration
mandu test
# The full pipeline including E2E
mandu test --e2e
# CI: fail fast + coverage
mandu test --coverage --bail
Under the hood mandu test wraps bun:test for unit and integration,
@mandujs/ate for E2E spec generation, and playwright test for the
browser leg — all with a single config block in mandu.config.ts.
Pages
- Unit —
mandu test unit, filling tests, fixtures,Symbol.disposefor mocks. - Integration —
createTestServer,createTestSession,createTestDb,mockMail,mockStorage. - E2E —
mandu test --e2e, the ATE pipeline, heal-on-failure. - Coverage —
--coverage, LCOV merge, threshold enforcement. - Watch —
--watch, affected-file heuristic, sub-second re-runs. - Snapshot —
toMatchSnapshot,--update-snapshots, golden-file conventions.
Config at a glance
Every knob lives under test in mandu.config.ts. Defaults work for a
fresh project — override only what you need:
// mandu.config.ts
import type { ManduConfig } from "@mandujs/core";
export default {
test: {
unit: {
include: ["**/*.test.ts", "**/*.test.tsx"],
exclude: ["node_modules/**", ".mandu/**"],
timeout: 30_000,
},
integration: {
include: ["tests/integration/**/*.test.ts"],
dbUrl: "sqlite::memory:",
sessionStore: "memory",
timeout: 60_000,
},
coverage: { lines: 80, branches: 70 },
},
} satisfies ManduConfig;
Unknown keys are rejected at load time — the schema is .strict() and
will tell you which keys are valid when you typo (e.g. uint vs unit).
Exit codes
| Code | Meaning |
|---|---|
0 |
All tests passed, or --dry-run plan |
1 |
At least one test failed |
2 |
Infra failure (spawn error, watcher I/O, timeout) |
4 |
Config error (schema, missing peer dep, etc.) |
🤖 Agent Prompt
Apply the guidance from the Mandu docs page at https://mandujs.com/docs/testing/index to my project.
Summary of the page:
Testing category hub. `mandu test` wraps bun:test for unit + integration + E2E (ATE/Playwright) with coverage merge, watch mode, and snapshot testing. Zero external deps required for unit/integration.
Required invariants — must hold after your changes:
- `mandu test` requires Bun >= 1.3.12 — the runner wraps `bun test` directly
- Test configuration lives under the `test` block of `mandu.config.ts` and is `.strict()` — unknown keys throw at load
- Exit code 0 = success, 1 = test failure, 2 = infra error, 4 = config error
- `--e2e` requires `@playwright/test` as a peer dependency (`bun add -d @playwright/test`)
- Merged LCOV always lands at `.mandu/coverage/lcov.info` (Phase 12.3)
Then:
1. Make the change in my codebase consistent with the page.
2. Run `bun run guard` and `bun run check` to verify nothing
in src/ or app/ breaks Mandu's invariants.
3. Show me the diff and any guard violations.
Related
- CLI Reference —
mandu test:auto— the ATE auto-generation entry point also exposed as its own command. - Build with Agents — ATE — the oracle levels + impact graph that drive E2E codegen.
- Recipes — Testing — copy-paste snippets for the common fixtures.
For Agents
Machine-readable contract for the testing surface.
{
"schema": "mandu.testing/v0.24",
"runner": "bun:test",
"e2e_runner": "playwright",
"config_root": "mandu.config.ts::test",
"commands": {
"all": "mandu test",
"unit": "mandu test unit",
"integration": "mandu test integration",
"e2e": "mandu test --e2e",
"coverage": "mandu test --coverage",
"watch": "mandu test --watch"
},
"exit_codes": {
"0": "success",
"1": "test failure",
"2": "infra error",
"4": "config/schema error"
},
"peer_dependencies": {
"e2e": "@playwright/test >= 1.40.0"
},
"coverage_output": ".mandu/coverage/lcov.info",
"rules": [
"Do not bypass `mandu test` by calling `bun test` directly — config normalization is lost",
"`mandu.config.ts::test` is `.strict()` — unknown keys throw on load",
"E2E without `@playwright/test` exits with CLI_E063"
]
}For Agents
Testing category hub. `mandu test` wraps bun:test for unit + integration + E2E (ATE/Playwright) with coverage merge, watch mode, and snapshot testing. Zero external deps required for unit/integration.
- `mandu test` requires Bun >= 1.3.12 — the runner wraps `bun test` directly
- Test configuration lives under the `test` block of `mandu.config.ts` and is `.strict()` — unknown keys throw at load
- Exit code 0 = success, 1 = test failure, 2 = infra error, 4 = config error
- `--e2e` requires `@playwright/test` as a peer dependency (`bun add -d @playwright/test`)
- Merged LCOV always lands at `.mandu/coverage/lcov.info` (Phase 12.3)