Migrating Tests from Jest to Bun
This guide provides instructions for migrating test files from Jest to Bun's test framework.
Table of Contents
Basic Setup
-
Remove Jest-related dependencies from
package.json
: -
Remove Jest configuration files:
jest.config.js
-
jest.setup.js
-
Update test scripts in
package.json
:
Import Changes
Before (Jest):
After (Bun):
import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test";
import type { Mock } from "bun:test";
Note: it
is replaced with test
in Bun.
API Changes
Test Structure
// Jest
describe('Suite', () => {
it('should do something', () => {
// test
});
});
// Bun
describe('Suite', () => {
test('should do something', () => {
// test
});
});
Assertions
Most Jest assertions work the same in Bun:
// These work the same in both:
expect(value).toBe(expected);
expect(value).toEqual(expected);
expect(value).toBeDefined();
expect(value).toBeUndefined();
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(array).toContain(item);
expect(value).toBeInstanceOf(Class);
expect(spy).toHaveBeenCalled();
expect(spy).toHaveBeenCalledWith(...args);
Mocking
Function Mocking
Before (Jest):
const mockFn = jest.fn();
mockFn.mockImplementation(() => 'result');
mockFn.mockResolvedValue('result');
mockFn.mockRejectedValue(new Error());
After (Bun):
const mockFn = mock(() => 'result');
const mockAsyncFn = mock(() => Promise.resolve('result'));
const mockErrorFn = mock(() => Promise.reject(new Error()));
Module Mocking
Before (Jest):
After (Bun):
// Option 1: Using vi.mock (if available)
vi.mock('module-name', () => ({
default: mock(() => {}),
namedExport: mock(() => {})
}));
// Option 2: Using dynamic imports
const mockModule = {
default: mock(() => {}),
namedExport: mock(() => {})
};
Mock Reset/Clear
Before (Jest):
After (Bun):
Spy on Methods
Before (Jest):
After (Bun):
Common Patterns
Async Tests
// Works the same in both Jest and Bun:
test('async test', async () => {
const result = await someAsyncFunction();
expect(result).toBe(expected);
});
Setup and Teardown
describe('Suite', () => {
beforeEach(() => {
// setup
});
afterEach(() => {
// cleanup
});
test('test', () => {
// test
});
});
Mocking Fetch
// Before (Jest)
global.fetch = jest.fn(() => Promise.resolve(new Response()));
// After (Bun)
const mockFetch = mock(() => Promise.resolve(new Response()));
global.fetch = mockFetch as unknown as typeof fetch;
Mocking WebSocket
// Create a MockWebSocket class implementing WebSocket interface
class MockWebSocket implements WebSocket {
public static readonly CONNECTING = 0;
public static readonly OPEN = 1;
public static readonly CLOSING = 2;
public static readonly CLOSED = 3;
public readyState: 0 | 1 | 2 | 3 = MockWebSocket.OPEN;
public addEventListener = mock(() => undefined);
public removeEventListener = mock(() => undefined);
public send = mock(() => undefined);
public close = mock(() => undefined);
// ... implement other required methods
}
// Use it in tests
global.WebSocket = MockWebSocket as unknown as typeof WebSocket;
Examples
Basic Test
import { describe, expect, test } from "bun:test";
describe('formatToolCall', () => {
test('should format an object into the correct structure', () => {
const testObj = { name: 'test', value: 123 };
const result = formatToolCall(testObj);
expect(result).toEqual({
content: [{
type: 'text',
text: JSON.stringify(testObj, null, 2),
isError: false
}]
});
});
});
Async Test with Mocking
import { describe, expect, test, mock } from "bun:test";
describe('API Client', () => {
test('should fetch data', async () => {
const mockResponse = { data: 'test' };
const mockFetch = mock(() => Promise.resolve(new Response(
JSON.stringify(mockResponse),
{ status: 200, headers: new Headers() }
)));
global.fetch = mockFetch as unknown as typeof fetch;
const result = await apiClient.getData();
expect(result).toEqual(mockResponse);
});
});
Complex Mocking Example
import { describe, expect, test, mock } from "bun:test";
import type { Mock } from "bun:test";
interface MockServices {
light: {
turn_on: Mock<() => Promise<{ success: boolean }>>;
turn_off: Mock<() => Promise<{ success: boolean }>>;
};
}
const mockServices: MockServices = {
light: {
turn_on: mock(() => Promise.resolve({ success: true })),
turn_off: mock(() => Promise.resolve({ success: true }))
}
};
describe('Home Assistant Service', () => {
test('should control lights', async () => {
const result = await mockServices.light.turn_on();
expect(result.success).toBe(true);
});
});
Best Practices
- Use TypeScript for better type safety in mocks
- Keep mocks as simple as possible
- Prefer interface-based mocks over concrete implementations
- Use proper type assertions when necessary
- Clean up mocks in
afterEach
blocks - Use descriptive test names
- Group related tests using
describe
blocks
Common Issues and Solutions
Issue: Type Errors with Mocks
// Solution: Use proper typing with Mock type
import type { Mock } from "bun:test";
const mockFn: Mock<() => string> = mock(() => "result");
Issue: Global Object Mocking
// Solution: Use type assertions carefully
global.someGlobal = mockImplementation as unknown as typeof someGlobal;