plugins/ls-typescript/skills/testing-bun/SKILL.md
Use when writing or modifying tests in a Bun project
npx skillsauth add LandonSchropp/agent-toolkit plugins/ls-typescript/skills/testing-bunInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
REQUIRED: fetch the Bun mocks documentation before writing or modifying tests.
Run the full suite via bun run test, never bun test directly. The script passes --isolate, which runs each test file in its own worker. Without it, mock.module() calls leak across files and tests fail in confusing, order-dependent ways.
For ad-hoc single-file runs, use bun test --isolate <file>.
Prefer the specific mock helpers over mockImplementation:
mockResolvedValue(value) / mockResolvedValueOnce(value) — async functions returning a value.mockRejectedValue(error) / mockRejectedValueOnce(error) — async functions that throw.mockReturnValue(value) / mockReturnValueOnce(value) — sync functions.Only reach for mockImplementation when behavior depends on arguments or call count.
Set mock return values close to the test that asserts on them, with the concrete value the test needs. Indirection through shared variables or transformations makes tests harder to read.
Never reassign properties on global or imported objects to stub them — use spyOn. Re-spy in beforeEach so each test starts from a known state.
// Good — spy, scoped to the test that needs it
import { spyOn } from "bun:test";
// Bad — reassigns the property
const original = Bun.stdin;
beforeEach(() => {
// @ts-expect-error
Bun.stdin = { json: () => Promise.resolve(payload) };
});
afterEach(() => {
// @ts-expect-error
Bun.stdin = original;
});
it("processes the payload", async () => {
spyOn(Bun.stdin, "json").mockResolvedValue({ hook_event_name: "Stop" });
// ... assertions
});
Use static imports — Bun's ESM live bindings let mock.module() update modules that have already been imported. No dynamic await import() needed.
mock.module() returns a promise, so it must be awaited:
import { isGitInstalled } from "../../src/commands/git.ts";
import { describe, expect, it, mock } from "bun:test";
const runCommandMock = mock(() => Promise.resolve({ exitCode: 0, stdout: "", stderr: "" }));
await mock.module("../../src/commands/shell.ts", () => ({
runCommand: runCommandMock,
}));
Clear mocks between tests with a global beforeEach in a shared setup file so individual test files don't need their own clearing logic.
tools
Use when a finished, reviewed branch is committed and needs to be merged into the default branch in a repo that integrates directly to `main` (not via pull request).
tools
Use when working with a stack of GitHub pull requests — creating branches, keeping the stack in sync, or merging in order. Covers Git Town setup, PR targeting, rebasing, and landing the stack.
tools
Use when publishing or releasing a new version of an npm/pnpm/yarn/bun package to the registry. Covers package-manager detection, semver bump selection, tagging, pushing, scoped-package access, authentication, and one-time passwords (OTP).
tools
Use when a finished worktree's branch has been reviewed and committed and needs to land. Rebases onto the latest default branch, then either fast-forwards it into the default branch (personal direct-to-main repos) or pushes it for a pull request (shared feature-branch repos).