mcpbeat

Bun Jest Migration

secondsky/bun-jest-migration

Use when migrating from Jest to Bun's test runner, import compatibility, mocks, and config.

This is a copy. The original lives at comeonoliver/bun-jest-migration.

1k tokens
context cost
the whole folder, loaded on every use
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
202
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/secondsky/claude-skills --skill bun-jest-migration

The instruction itself

21 sections, as written by the author

Bun Jest Migration

Bun's test runner is Jest-compatible. Most Jest tests run without changes.

Quick Migration

# 1. Remove Jest dependencies
bun remove jest ts-jest @types/jest babel-jest

# 2. Update test script
# package.json: "test": "bun test"

# 3. Run tests
bun test

Import Changes

// Before (Jest)
import { describe, it, expect, jest } from '@jest/globals';

// After (Bun) - No import needed, or explicit:
import { describe, test, expect, mock, spyOn } from "bun:test";

API Compatibility

Fully Compatible

| Jest | Bun | Notes |

|------|-----|-------|

| describe() | describe() | Identical |

| it() / test() | test() | Use test() |

| expect() | expect() | Same matchers |

| beforeAll/Each | beforeAll/Each | Identical |

| afterAll/Each | afterAll/Each | Identical |

| jest.fn() | mock() | Use mock() |

| jest.spyOn() | spyOn() | Identical |

Requires Changes

| Jest | Bun Equivalent |

|------|----------------|

| jest.mock('module') | mock.module('module', () => {...}) |

| jest.useFakeTimers() | import { setSystemTime } from "bun:test" |

| jest.setTimeout() | Third argument to test() |

| jest.clearAllMocks() | Call .mockClear() on each mock |

Mock Migration

Mock Functions

// Jest
const fn = jest.fn().mockReturnValue('value');

// Bun
const fn = mock(() => 'value');
// Or for compatibility:
import { jest } from "bun:test";
const fn = jest.fn(() => 'value');

Module Mocking

// Jest (top-level hoisting)
jest.mock('./utils', () => ({
  helper: jest.fn(() => 'mocked')
}));

// Bun (inline, no hoisting)
import { mock } from "bun:test";
mock.module('./utils', () => ({
  helper: mock(() => 'mocked')
}));

Spy Migration

// Jest
jest.spyOn(console, 'log').mockImplementation(() => {});

// Bun (identical)
spyOn(console, 'log').mockImplementation(() => {});

Timer Migration

// Jest
jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-01'));
jest.advanceTimersByTime(1000);

// Bun - supports Jest-compatible timer APIs
import { setSystemTime } from "bun:test";
import { jest } from "bun:test";

jest.useFakeTimers();
jest.setSystemTime(new Date('2024-01-01'));
jest.advanceTimersByTime(1000);  // Now supported

Snapshot Testing

// Jest
expect(component).toMatchSnapshot();
expect(data).toMatchInlineSnapshot(`"expected"`);

// Bun (identical)
expect(component).toMatchSnapshot();
expect(data).toMatchInlineSnapshot(`"expected"`);

Update snapshots:

bun test --update-snapshots

Configuration Migration

jest.config.js → bunfig.toml

// jest.config.js (before)
module.exports = {
  testMatch: ['**/*.test.ts'],
  testTimeout: 10000,
  setupFilesAfterEnv: ['./jest.setup.ts'],
  collectCoverage: true,
  coverageThreshold: { global: { lines: 80 } }
};
# bunfig.toml (after)
[test]
root = "./"
preload = ["./jest.setup.ts"]
timeout = 10000
coverage = true
coverageThreshold = 0.8

Common Migration Issues

Issue: jest.mock Not Working

// Jest mock hoisting doesn't exist in Bun
// Move mock.module before imports or use dynamic imports

// Solution 1: Use mock.module at top
mock.module('./api', () => ({ fetch: mock() }));
import { fetch } from './api';

// Solution 2: Dynamic import
const mockFetch = mock();
mock.module('./api', () => ({ fetch: mockFetch }));
const { fetch } = await import('./api');

Issue: Timer Functions Missing

// Bun timer support is limited
// Use setSystemTime for date mocking
import { setSystemTime } from "bun:test";

beforeEach(() => {
  setSystemTime(new Date('2024-01-01'));
});

afterEach(() => {
  setSystemTime(); // Reset to real time
});

Issue: Custom Matchers

// Jest
expect.extend({ toBeWithinRange(received, floor, ceiling) {...} });

// Bun (same API)
import { expect } from "bun:test";
expect.extend({
  toBeWithinRange(received, floor, ceiling) {
    const pass = received >= floor && received <= ceiling;
    return {
      pass,
      message: () => `expected ${received} to be within ${floor}-${ceiling}`
    };
  }
});

Step-by-Step Migration

  • Remove Jest packages
   bun remove jest ts-jest @types/jest babel-jest jest-environment-jsdom
  • Update package.json
   {
     "scripts": {
       "test": "bun test",
       "test:watch": "bun test --watch",
       "test:coverage": "bun test --coverage"
     }
   }
  • Convert jest.config.js to bunfig.toml
  • Update imports in test files
  • Find/replace @jest/globalsbun:test
  • Find/replace jest.fn()mock()
  • Find/replace jest.mock()mock.module()
  • Run and fix
   bun test 2>&1 | head -50  # Check first errors

Common Errors

| Error | Cause | Fix |

|-------|-------|-----|

| Cannot find module '@jest/globals' | Old import | Use bun:test |

| jest is not defined | Global jest | Import from bun:test |

| mock.module is not a function | Wrong import | import { mock } from "bun:test" |

| Snapshot mismatch | Different serialization | Update with --update-snapshots |

When to Load References

Load references/compatibility-matrix.md when:

  • Full Jest API compatibility details
  • Unsupported features list
  • Workarounds for missing features

How to use it

Copy the folder

Take secondsky/bun-jest-migration from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.