04-bun-test
Bun built-in test runner — zero-config testing for Effect, React, and TypeScript projects
- License 许可
- MIT
- 最近更新
- 2026-05-31
- Stars
- ★ 0
- Forks
- 0
01 前言 Front Matter
- name
- 04-bun-test
- license
- MIT
- description
- Bun built-in test runner — zero-config testing for Effect, React, and TypeScript projects
- compatibility
- opencode
03 SKILL.md 正文
Bun Test
Package: Built into Bun (no installation needed)
Command: bun test
Bun's built-in test runner replaces Vitest/Jest with zero configuration. Same API, faster execution, no extra dependencies.
Why bun:test over Vitest
| Feature | bun:test | Vitest |
|---|---|---|
| Installation | Built-in | npm i -D vitest |
| Config file | None needed | vitest.config.ts |
| Startup | Instant | ~1-2s |
| Effect support | Native | Via plugin |
| DOM testing | Requires happy-dom | Built-in via browser mode |
Rule of thumb: Use bun:test for all logic/server tests. Use Storybook for visual component inspection. Skip DOM-heavy component tests.
Basic Test
import { describe, expect, test } from "bun:test";
describe("fmtModel", () => {
test("strips openrouter/ prefix", () => {
expect(fmtModel("openrouter/google/gemini-2.5-pro")).toBe("google/gemini-2.5-pro");
});
});
Effect Tests
import { describe, expect, test } from "bun:test";
import { Effect } from "effect";
describe("TaskService", () => {
test("getTask returns cached task", async () => {
const program = Effect.gen(function* () {
const svc = yield* TaskService;
return yield* svc.getTask("bool_not");
});
const result = await Effect.runPromise(program.pipe(Effect.provide(TaskServiceLive)));
expect(result.id).toBe("bool_not");
});
});
Async Tests
test("async operation", async () => {
const result = await fetchData();
expect(result).toBeDefined();
});
Snapshots
test("snapshot comparison", () => {
expect(data).toMatchSnapshot();
});
Update snapshots: bun test -u
Skipping and TODO
test.skip("not ready yet", () => { ... });
test.todo("implement this later");
test.only("run only this", () => { ... });
Test Files
Bun automatically discovers:
*.test.ts*.test.tsx*.spec.ts*.spec.tsx
React Component Testing
Philosophy: Test logic, not DOM. Use Storybook for visual inspection.
// ❌ Don't test DOM rendering
import { render } from "@testing-library/react";
const { container } = render(<BarChart pct={50} />);
expect(container.querySelector("span")).toBeDefined();
// ✅ Test pure logic
function getBarColor(pct: number): string {
if (pct >= 70) return "green";
if (pct >= 45) return "blue";
if (pct >= 20) return "yellow";
return "red";
}
describe("getBarColor", () => {
test("green at 70+", () => {
expect(getBarColor(70)).toBe("green");
expect(getBarColor(100)).toBe("green");
});
test("blue at 45-69", () => {
expect(getBarColor(45)).toBe("blue");
expect(getBarColor(69)).toBe("blue");
});
test("yellow at 20-44", () => {
expect(getBarColor(20)).toBe("yellow");
expect(getBarColor(44)).toBe("yellow");
});
test("red below 20", () => {
expect(getBarColor(0)).toBe("red");
expect(getBarColor(19)).toBe("red");
});
});
Mocking
import { mock, spyOn } from "bun:test";
// Mock a function
const mockFn = mock((x: number) => x * 2);
expect(mockFn(5)).toBe(10);
expect(mockFn).toHaveBeenCalledTimes(1);
// Spy on module
import * as fs from "node:fs";
const readSpy = spyOn(fs, "readFileSync").mockReturnValue("test");
// Restore
readSpy.mockRestore();
Running Tests
# All tests
bun test
# Specific file
bun test src/lib/fmt.test.ts
# Watch mode
bun test --watch
# Update snapshots
bun test -u
# Coverage
bun test --coverage
# Bail on first failure
bun test --bail
Package.json Scripts
{
"scripts": {
"test": "bun test",
"test:watch": "bun test --watch",
"test:coverage": "bun test --coverage"
}
}
CI Integration
# .github/workflows/test.yml
- name: Run tests
run: bun test
No config file needed. No setup files. Just bun test.
Comparison: When to Use What
| Test Type | Tool | Example |
|---|---|---|
| Pure functions | bun:test |
fmtModel(), getBarColor() |
| Effect services | bun:test |
TaskService.getTask() |
| React logic | bun:test |
Hook utilities, helpers |
| Visual appearance | Storybook | Component fixtures |
| Browser behavior | Manual QA | Click flows, responsive |
| E2E API | bun:test + fetch |
HTTP endpoint tests |
Migrating from Vitest
- Replace
import { ... } from "vitest"withimport { ... } from "bun:test" - Replace
vi.fn()withmock() - Replace
vi.spyOn()withspyOn() - Remove
vitest.config.ts - Remove
@vitest/*dependencies - Convert DOM tests to logic tests or Storybook fixtures
- Update package.json scripts
Anti-Patterns
// ❌ Testing implementation details
test("sets internal state", () => { ... });
// ❌ Testing React DOM structure
test("has 3 divs", () => { ... });
// ❌ Heavy mocking
mock("./module", () => ({ ... }));
// ✅ Test behavior
// ✅ Test pure logic
// ✅ Test public API
// ✅ Visual inspection in Storybook
04 同仓库更多 Skills
tmux-workflow
Development workflow with tmux — run dev servers, Storybook, and background processes in persistent sessions with live logs, while keeping OpenCode free for chat
04-anti-patterns
React anti-patterns to avoid — manual JSX repetition, useEffect misuse, reference instability, prop drilling, conditional hooks, and common performance traps
03-state-management
React state management patterns — lifting state, colocation, URL state, server state with TanStack Query, local state with useState/useReducer
02-component-composition
React component composition patterns — render props, compound components, list rendering with map, slot patterns, and layout primitives
01-fp-style
React functional programming style — pure components, hooks conventions, Effect Atom integration, and Tailwind patterns
react-animation
React animation with react/motion (Framer Motion) — declarative animations, gestures, layout animations, and performance best practices