# Runner — vitest / bun-test conditional re-exports _(no submodules)_ - vitest — The `runner/vitest` module re-exports vite… - [vitest](./vitest.md) — The `runner/vitest` module re-exports vitest's `describe`, `it`, `test`, `expect`, `beforeAll`, `beforeEach`, `afterAll… - vitest — The `runner/vitest` module re-exports vite… - [vitest](./vitest.md) — The `runner/vitest` module re-exports vitest's `describe`, `it`, `test`, `expect`, `beforeAll`, `beforeEach`, `afterAll… - vitest — The `runner/vitest` module re-exports vite… - [vitest](./vitest.md) — The `runner/vitest` module re-exports vitest's `describe`, `it`, `test`, `expect`, `beforeAll`, `beforeEach`, `afterAll… - vitest — The `runner/vitest` module re-exports vite… - [vitest](./vitest.md) — The `runner/vitest` module re-exports vitest's `describe`, `it`, `test`, `expect`, `beforeAll`, `beforeEach`, `afterAll… - [vitest](./vitest.md) — The `runner/vitest` module re-exports vitest's `describe`, `it`, `test`, `expect`, `beforeAll`, `beforeEach`, `afterAll… ## Purpose The runner barrel re-exports the test primitives (`describe`, `it`, `test`, `expect`, `beforeAll`, `beforeEach`, `afterAll`, `afterEach`, `vi`) plus a runner-neutral mock surface (`fn`, `spy`, `stub`, `mock`, `fakeClock`, `restoreMocks`, `restoreTimers`). Two backends are supported: vitest (the default) and bun's built-in `bun:test`. Which backend resolves at import time is driven by `package.json` conditional exports — under the `bun` condition Node resolves `./dist/runner/bun-test.js`; under `vitest` or `default` it resolves `./dist/index.js` which barrels in `vitest.ts`. The mock helpers exist so test code can stay runner-agnostic. A package that uses `fn()` / `spy()` / `stub()` instead of importing `vi` directly will run unchanged when its consumers swap test runners — no test-source rewrites required. This is the pattern the original `2026-04-08-testing-libraries` change established and we preserve it here. ## Features - Re-exports of `vitest` primitives under the default / vitest conditions - `bun:test` re-exports under the bun condition with API-shaped equivalents (`vi` is aliased to bun's `jest` namespace) - Runner-neutral mock helpers: `fn`, `spy`, `stub`, `mock(modulePath)`, `fakeClock`, `restoreMocks`, `restoreTimers` - `mock(modulePath, factory?)` returns a `{ restore() }` handle for symmetry across runners - `fakeClock()` exists for legacy compatibility — new code should prefer the deterministic `FakeClock` class from `@suaveplan/testing/clock/fake-clock` ## Usage ### Basic example — runner-neutral test ```ts import { describe, it, expect, fn, spy, } from "@suaveplan/testing"; describe("user service", () => { it("calls the dependency exactly once", { timeout: 5000 }, () => { const dep = { fetch: () => "real" }; const handle = spy(dep, "fetch"); const callback = fn(); callback("hello"); expect(callback).toHaveBeenCalledWith("hello"); expect(handle).toHaveBeenCalledTimes(0); }); }); ``` ### Advanced example — module mocking with restore ```ts import { mock, restoreMocks } from "@suaveplan/testing"; const handle = mock("./network.js", () => ({ fetchUser: () => ({ id: 42 }), })); // run tests against the mocked module ... handle.restore(); restoreMocks(); ``` ### Switching to bun-test In a consumer's `package.json`, set the `bun` condition (or run via `bun test`). Resolution picks `@suaveplan/testing/dist/runner/bun-test.js` automatically; no test-source changes are needed. ## API Reference ### Test primitives `describe`, `it`, `test`, `expect`, `beforeAll`, `beforeEach`, `afterAll`, `afterEach`, `vi` — re-exported from the active runner. ### Mock helpers - `fn(implementation?): Mock` - `spy(obj: T, method: K): SpyHandle` - `stub(obj: T, method: K, implementation?): SpyHandle` - `mock(modulePath: string, factory?: () => unknown): { restore() }` - `fakeClock(): { advance(ms), restore() }` - `restoreMocks(): void` - `restoreTimers(): void` ## Implementation Notes - **`vi` on the bun path is aliased to `jest`.** Bun-test's mock surface lives under `jest` for API compatibility; the alias keeps consumer code identical across runners. - **`fakeClock` is legacy.** Prefer the deterministic `FakeClock` class from the clock module — it's runner-independent, supports `Clock` injection across the package, and gives you `snapshot()` / `restore()` for fine control. - **`mock()` on bun has a no-op `restore()`** because `bun:test` does not expose `unmock`. Call `restoreMocks()` (which delegates to `jest.restoreAllMocks()`) to reset state. - **The conditional-exports routing** is load-bearing — see `package.json#exports.".".bun` and `.vitest`. Removing either branch breaks the dual-runner contract documented in the spec. - **Type safety on cross-runner mocks** is approximate. The wrappers use `as never` casts internally because the spy/stub generics differ between the two libraries; consumer code sees a stable shape.