Skip to main content

outfitter-testing

Testing patterns for @outfitter/* packages using test harnesses, fixtures, and Bun test runner. Use when writing tests, setting up test harnesses, mocking context, or when "test handler", "test CLI", "test MCP", or "@outfitter/testing" are mentioned.

Quellinformationen

Repository
outfitter-dev/outfitter
Letzte Quellaktivität
24. Februar 2026 um 23:09
Erkannte Sprache von SKILL.md
Englisch
Sterne
6
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
outfitter-testing
version
0.1.0
description
Testing patterns for @outfitter/* packages using test harnesses, fixtures, and Bun test runner. Use when writing tests, setting up test harnesses, mocking context, or when "test handler", "test CLI", "test MCP", or "@outfitter/testing" are mentioned.
allowed-tools
Read Write Edit Glob Grep Bash(bun test *)
# Stack Testing Test patterns for @outfitter/\* packages. ## Test Structure ``` src/ ├── handlers/ │ └── get-user.ts └── __tests__/ ├── get-user.test.ts └── __snapshots__/ └── get-user.test.ts.snap ``` ## Handler Testing Test handlers directly without transport layer: ```typescript import { describe, test, expect } from "bun:test"; import { createContext } from "@outfitter/contracts"; import { getUser } from "../handlers/get-user.js"; describe("getUser", () => { test("returns user when found", async () => { const ctx = createContext({}); const result = await getUser({ id: "user-1" }, ctx); expect(result.isOk()).toBe(true); expect(result.value).toEqual({ id: "user-1", name: "Alice", }); }); test("returns NotFoundError when user missing", async () => { const ctx = createContext({}); const result = await getUser({ id: "missing" }, ctx); expect(result.isErr()).toBe(true); expect(result.error._tag).toBe("NotFoundError"); expect(result.error.resourceId).toBe("missing"); }); test("returns ValidationError for invalid input", async () => { const ctx = createContext({}); const result = await getUser({ id: "" }, ctx); expect(result.isErr()).toBe(true); expect(result.error._tag).toBe("ValidationError"); }); }); ``` ## Test Fixtures Use `createFixture` for deep-merged test data: ```typescript import { createFixture } from "@outfitter/testing"; interface User { id: string; name: string; email: string; settings: { theme: string; notifications: boolean }; } const createUser = createFixture<User>({ id: "user-1", name: "Test User", email: "test@example.com", settings: { theme: "light", notifications: true }, }); test("user with custom settings", async () => { const user = createUser({ settings: { theme: "dark" } }); // user.settings.notifications is still true (deep merge) }); ``` ## Temporary Directories Use `withTempDir` for isolated file operations: ```typescript import { withTempDir } from "@outfitter/testing"; test("writes config file", async () => { await withTempDir(async (dir) => { const result = await writeConfig({ dir, data: { key: "value" } }, ctx); expect(result.isOk()).toBe(true); const content = await Bun.file(`${dir}/config.json`).json(); expect(content).toEqual({ key: "value" }); }); }); ``` ## Environment Mocking Use `withEnv` for environment variable testing: ```typescript import { withEnv } from "@outfitter/testing"; test("uses custom log level", async () => { await withEnv({ LOG_LEVEL: "debug" }, async () => { const config = loadConfig(); expect(config.logLevel).toBe("debug"); }); }); ``` ## CLI Testing Use `createCliHarness` for CLI command testing: ```typescript import { createCliHarness } from "@outfitter/testing"; import { listCommand } from "../commands/list.js"; const harness = createCliHarness(listCommand); test("lists items in JSON mode", async () => { const result = await harness.run(["--json"]); expect(result.exitCode).toBe(0); expect(result.stdout).toContain('"items"'); }); test("exits with error for invalid flag", async () => { const result = await harness.run(["--invalid"]); expect(result.exitCode).toBe(1); expect(result.stderr).toContain("Unknown option"); }); ``` ## MCP Testing Use `createMcpHarness` for MCP tool testing: ```typescript import { createMcpHarness } from "@outfitter/testing"; import { searchTool } from "../tools/search.js"; const harness = createMcpHarness(searchTool); test("returns search results", async () => { const result = await harness.invoke({ query: "test", limit: 10, }); expect(result.isOk()).toBe(true); expect(result.value.results).toHaveLength(3); }); test("validates input schema", async () => { const result = await harness.invoke({ query: "", // Invalid: min length 1 }); expect(result.isErr()).toBe(true); expect(result.error._tag).toBe("ValidationError"); }); ``` ## Context Mocking Create mock context with logger spy: ```typescript import { createContext } from "@outfitter/contracts"; import { createMockLogger } from "@outfitter/testing"; test("logs debug messages", async () => { const mockLogger = createMockLogger(); const ctx = createContext({ logger: mockLogger }); await myHandler({ id: "1" }, ctx); expect(mockLogger.calls.debug).toContainEqual(["Processing", { id: "1" }]); }); ``` ## Snapshot Testing Use Bun's snapshot testing: ```typescript import { expect, test } from "bun:test"; test("output matches snapshot", async () => { const result = await formatOutput(data); expect(result).toMatchSnapshot(); }); ``` Snapshots stored in `__snapshots__/*.snap`. ## Result Assertions Custom matchers for Result types: ```typescript // Check success expect(result.isOk()).toBe(true); expect(result.value).toEqual(expected); // Check failure expect(result.isErr()).toBe(true); expect(result.error._tag).toBe("NotFoundError"); expect(result.error.category).toBe("not_found"); // Error context expect(result.error.context).toMatchObject({ field: "email", }); ``` ## Running Tests ```bash # All tests bun test # Single file bun test src/__tests__/get-user.test.ts # Watch mode bun test --watch # With coverage bun test --coverage # Update snapshots bun test --update-snapshots ``` ## Best Practices 1. **Test handlers directly** — Skip transport layer for unit tests 2. **Use fixtures** — Create reusable test data with `createFixture` 3. **Isolate side effects** — Use `withTempDir` and `withEnv` 4. **Mock context** — Inject mock logger to verify logging 5. **Test error paths** — Verify correct error types and categories 6. **Snapshot outputs** — Use snapshots for complex output verification ## Related Skills - `stack:patterns` — Handler contract reference - `stack:scaffold` — Handler templates - `stack:debug` — Troubleshooting test failures
Auf GitHub ansehen