Skip to content

# 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
- Import Changes
- API Changes
- Mocking
- Common Patterns
- Examples

## Basic Setup

1. Remove Jest-related dependencies from `package.json`:

       {
         "devDependencies": {
           "@jest/globals": "...",
           "jest": "...",
           "ts-jest": "..."
         }
       }

2. Remove Jest configuration files:

3. `jest.config.js`

4. `jest.setup.js`

5. Update test scripts in `package.json`:

       {
         "scripts": {
           "test": "bun test",
           "test:watch": "bun test --watch",
           "test:coverage": "bun test --coverage"
         }
       }

## Import Changes

### Before (Jest):

    import { jest, describe, it, expect, beforeEach, afterEach } from '@jest/globals';

### 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):

    jest.mock('module-name', () => ({
      default: jest.fn(),
      namedExport: jest.fn()
    }));

#### 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):

    jest.clearAllMocks();
    mockFn.mockClear();
    jest.resetModules();

#### After (Bun):

    mockFn.mockReset();
    // or for specific calls
    mockFn.mock.calls = [];

### Spy on Methods

#### Before (Jest):

    jest.spyOn(object, 'method');

#### After (Bun):

    const spy = mock(((...args) => object.method(...args)));
    object.method = spy;

## 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

1. Use TypeScript for better type safety in mocks
2. Keep mocks as simple as possible
3. Prefer interface-based mocks over concrete implementations
4. Use proper type assertions when necessary
5. Clean up mocks in `afterEach` blocks
6. Use descriptive test names
7. 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;

### Issue: Module Mocking

    // Solution: Use dynamic imports or vi.mock if available
    const mockModule = {
      default: mock(() => mockImplementation)
    };

February 5, 2025
February 5, 2025
Was this page helpful?
Thanks for your feedback!
Thanks for your feedback! Please consider creating an issue to help us improve.

#### Cookie consent

We use cookies to recognize your repeated visits and preferences, as well as to measure the effectiveness of our documentation and whether users find what they're searching for. With your consent, you're helping us to make our documentation better.

- Google Analytics
- GitHub

Manage settings