Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
ainergiz avatar

Bun Test

  • 7 installs
  • 11 repo stars
  • Updated March 8, 2026
  • ainergiz/xfeed

bun-test is a Claude Code skill that teaches writing and debugging tests with Bun's bun:test runner, including mocking and coverage.

About

bun-test is a Claude Code skill that teaches writing and debugging tests with Bun's built-in test runner. It covers mock functions, spies, fetch and module mocking, per-test temp-directory isolation, and coverage reporting. Developers use it when writing tests, fixing test failures, or setting up test infrastructure in a Bun project.

  • Bun test patterns: mocking, spies, module mocks, and coverage
  • fetch and module mocking with restore-after-test isolation
  • Temp-directory and state isolation to avoid test races

Bun Test by the numbers

  • 7 all-time installs (skills.sh)
  • Ranked #1,579 of 2,154 Testing & QA skills by installs in the Skillselion catalog
  • Data as of Jul 28, 2026 (Skillselion catalog sync)
At a glance

bun-test capabilities & compatibility

Capabilities
unit testing · test mocking · coverage reporting
Use cases
testing · debugging
From the docs

What bun-test says it does

Write and debug Bun tests with proper mocking, coverage, and isolation.
SKILL.md
Use `mkdtemp()` per test, not a shared temp directory:
SKILL.md
npx skills add https://github.com/ainergiz/xfeed --skill bun-test

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs7
repo stars11
Last updatedMarch 8, 2026
Repositoryainergiz/xfeed

What it does

Write and debug Bun tests with mocking, isolation, and coverage.

Who is it for?

Developers writing or debugging tests in a Bun project.

Skip if: Projects that use Jest, Vitest, or a non-Bun runtime.

When should I use this skill?

When writing tests, debugging test failures, setting up test infrastructure, or mocking fetch/modules.

What you get

Isolated, correctly mocked Bun tests with meaningful coverage.

  • Bun test files
  • coverage reports

By the numbers

  • aim for 99%+ coverage
  • mkdtemp per test for isolation

Files

SKILL.mdMarkdownGitHub ↗

Bun Test Guide

Quick Start

import { describe, expect, it, mock, spyOn, beforeEach, afterEach, afterAll } from "bun:test";

describe("MyModule", () => {
  it("does something", () => {
    expect(1 + 1).toBe(2);
  });
});

Run tests:

bun test                    # Run all tests
bun test --watch            # Watch mode
bun test --coverage         # With coverage report
bun test src/api            # Specific directory
bun test --test-name-pattern "pattern"  # Filter by name

Mocking Patterns

Mock Functions

const mockFn = mock(() => "mocked value");
mockFn();
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(1);

// Reset between tests
mockFn.mockReset();
mockFn.mockImplementation(() => "new value");

Spy on Object Methods

import { myModule } from "./my-module";

let methodSpy: Mock<typeof myModule.method>;

beforeAll(() => {
  methodSpy = spyOn(myModule, "method").mockImplementation(() => "mocked");
});

afterAll(() => {
  methodSpy.mockRestore(); // IMPORTANT: Always restore spies
});

Mock fetch (globalThis.fetch)

Bun's fetch has extra properties (like preconnect) that mocks don't have. Use // @ts-nocheck at file top for test files with fetch mocking:

// @ts-nocheck - Test file with fetch mocking
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";

const originalFetch = globalThis.fetch;

// Helper for mock responses
function mockResponse(body: unknown, options: { status?: number; ok?: boolean } = {}) {
  const status = options.status ?? 200;
  const ok = options.ok ?? (status >= 200 && status < 300);
  return {
    ok,
    status,
    text: () => Promise.resolve(typeof body === "string" ? body : JSON.stringify(body)),
    json: () => Promise.resolve(body),
  } as Response;
}

describe("API", () => {
  afterEach(() => {
    globalThis.fetch = originalFetch; // Always restore
  });

  it("fetches data", async () => {
    globalThis.fetch = mock(() => Promise.resolve(mockResponse({ data: "test" })));

    const result = await myApi.getData();
    expect(result).toEqual({ data: "test" });
  });

  it("handles errors", async () => {
    globalThis.fetch = mock(() => Promise.reject(new Error("Network error")));

    await expect(myApi.getData()).rejects.toThrow("Network error");
  });
});

Mock Modules (External Dependencies)

Use mock.module() BEFORE importing the module under test:

// @ts-nocheck - Test file with module mocking
import { describe, expect, it, mock } from "bun:test";

// Create mutable mock implementation
let mockImpl = () => Promise.resolve({ data: "default" });

// Mock the module BEFORE importing
mock.module("external-package", () => ({
  someFunction: () => mockImpl(),
}));

// NOW import the module that uses external-package
const { myFunction } = await import("./my-module");

// Helper to change mock behavior per test
function setMockReturn(value: unknown) {
  mockImpl = () => Promise.resolve(value);
}

describe("MyModule", () => {
  it("uses external package", async () => {
    setMockReturn({ data: "test" });
    const result = await myFunction();
    expect(result.data).toBe("test");
  });
});

Test Isolation

State Sharing Warning

Tests within a file share module-level state. Use setup/teardown hooks carefully:

// Store original values at module level
const originalEnv = process.env.NODE_ENV;
const originalFetch = globalThis.fetch;

afterAll(() => {
  // Restore everything
  globalThis.fetch = originalFetch;
  if (originalEnv !== undefined) {
    process.env.NODE_ENV = originalEnv;
  } else {
    delete process.env.NODE_ENV;
  }
});

Temp Directory Isolation

Use mkdtemp() per test, not a shared temp directory:

import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";

let testDir: string;

beforeEach(async () => {
  // Unique temp dir per test - avoids race conditions
  testDir = await mkdtemp(path.join(tmpdir(), "my-test-"));
});

afterEach(async () => {
  if (testDir) {
    await rm(testDir, { recursive: true, force: true }).catch(() => {});
  }
});

Coverage

bun test --coverage              # Generate text report
bun test --coverage-reporter lcov # For CI/tooling integration

Coverage Quirks

1. Closing braces after return may show uncovered even when executed 2. Function declarations may not count if only body runs 3. 100% may be impossible - aim for 99%+ on meaningful code

Improving Coverage

  • Test all branches (if/else, switch cases)
  • Test error paths and edge cases
  • Test with different input types
  • Don't obsess over unreachable code (closing braces, etc.)

Common Patterns

Async Tests

it("handles async", async () => {
  const result = await asyncFunction();
  expect(result).toBe("expected");
});

it("expects rejection", async () => {
  await expect(asyncFunction()).rejects.toThrow("error message");
});

Parameterized Tests

const testCases = [
  { input: 1, expected: 2 },
  { input: 2, expected: 4 },
];

for (const { input, expected } of testCases) {
  it(`doubles ${input} to ${expected}`, () => {
    expect(double(input)).toBe(expected);
  });
}

Testing Timeouts

it("handles timeout", async () => {
  // Use small delays for tests
  const result = await functionWithDelay(1); // 1ms instead of 1000ms
  expect(result).toBeDefined();
});

Checklist for New Test Files

1. Add // @ts-nocheck if mocking fetch or complex types 2. Store original values (fetch, env vars) before modifying 3. Restore everything in afterEach or afterAll 4. Use mkdtemp() for temp directories (not shared paths) 5. Call mockRestore() on spies in afterAll 6. Use descriptive test names that explain the scenario

Debugging Tests

bun test --bail                  # Stop on first failure
bun test --timeout 30000         # Increase timeout (ms)
bun test --test-name-pattern "specific test"  # Run one test

Add console.log for debugging (remove before committing):

it("debugging", () => {
  console.log("Value:", someValue);
  expect(someValue).toBeDefined();
});

Additional Resources

For xfeed-specific patterns (XClient, RuntimeQueryIdStore, cookie mocking, GraphQL responses), see PATTERNS.md.

Related skills

FAQ

How do I mock fetch in Bun?

Save globalThis.fetch, replace it with a mock returning a Response-like object, and restore it in afterEach.

How do I isolate temp directories?

Use mkdtemp() per test rather than a shared temp directory to avoid race conditions.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.