Skip to content
intermediate Phase 12 · Testing

Mocking

Mock APIs, modules, and browser APIs for isolated testing.

30m
0 problems
Topic Progress 0%

Mocking APIs

Mocking APIs

MSW (Recommended)

// src/mocks/handlers.js
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/api/users', () => {
    return HttpResponse.json([
      { id: 1, name: 'John' },
      { id: 2, name: 'Jane' },
    ]);
  }),

  http.post('/api/users', async ({ request }) => {
    const body = await request.json();
    return HttpResponse.json({ id: 3, ...body }, { status: 201 });
  }),

  http.get('/api/users/:id', ({ params }) => {
    return HttpResponse.json({ id: params.id, name: 'John' });
  }),
];

Mock Service Worker Setup

// src/mocks/server.js
import { setupServer } from 'msw/node';
import { handlers } from './handlers';

export const server = setupServer(...handlers);

// src/test/setup.js
import { server } from '../mocks/server';

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

Custom Handlers in Tests

describe('UserList', () => {
  it('should handle empty list', async () => {
    server.use(
      http.get('/api/users', () => {
        return HttpResponse.json([]);
      })
    );

    render(<UserList />);

    await waitFor(() => {
      expect(screen.getByText('No users found')).toBeInTheDocument();
    });
  });

  it('should handle error', async () => {
    server.use(
      http.get('/api/users', () => {
        return HttpResponse.json(null, { status: 500 });
      })
    );

    render(<UserList />);

    await waitFor(() => {
      expect(screen.getByText('Error loading users')).toBeInTheDocument();
    });
  });
});

Fetch Mock

// Simple fetch mock
beforeEach(() => {
  global.fetch = jest.fn();
});

afterEach(() => {
  jest.restoreAllMocks();
});

it('should fetch data', async () => {
  global.fetch.mockResolvedValueOnce({
    ok: true,
    json: async () => ({ data: 'test' }),
  });

  const result = await fetchData();

  expect(result).toEqual({ data: 'test' });
  expect(global.fetch).toHaveBeenCalledWith('/api/data');
});

Mocking Modules

Mocking Modules

jest.mock

// Mock entire module
jest.mock('./api');
import { fetchUsers } from './api';

beforeEach(() => {
  fetchUsers.mockClear();
});

it('should call fetchUsers', async () => {
  fetchUsers.mockResolvedValue([{ id: 1, name: 'John' }]);

  render(<UserList />);

  await waitFor(() => {
    expect(fetchUsers).toHaveBeenCalled();
  });
});

Partial Mock

// Mock specific methods
jest.mock('./utils', () => ({
  ...jest.requireActual('./utils'),
  formatDate: jest.fn(() => '2024-01-01'),
}));

import { formatDate, otherFunction } from './utils';

it('should use mocked formatDate', () => {
  expect(formatDate()).toBe('2024-01-01');
  // otherFunction still uses real implementation
});

Mock Component

// Mock child component
jest.mock('./ChildComponent', () => {
  return function MockChild({ name }) {
    return <div data-testid="mock-child">Mock: {name}</div>;
  };
});

// Mock with factory
jest.mock('./HeavyComponent', () => {
  return {
    __esModule: true,
    default: jest.fn(() => <div>Mocked</div>),
    helperFunction: jest.fn(() => 'mocked'),
  };
});

Mock Router

// Mock react-router-dom
const mockNavigate = jest.fn();
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useNavigate: () => mockNavigate,
  useParams: () => ({ id: '1' }),
}));

it('should navigate on click', () => {
  render(<MyComponent />);
  fireEvent.click(screen.getByText('Go to page'));
  expect(mockNavigate).toHaveBeenCalledWith('/page/1');
});

Mocking Browser APIs

Mocking Browser APIs

window.matchMedia

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation(query => ({
    matches: query === '(prefers-color-scheme: dark)',
    media: query,
    onchange: null,
    addListener: jest.fn(),
    removeListener: jest.fn(),
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

IntersectionObserver

const mockIntersectionObserver = jest.fn();
mockIntersectionObserver.mockReturnValue({
  observe: jest.fn(),
  unobserve: jest.fn(),
  disconnect: jest.fn(),
});

window.IntersectionObserver = mockIntersectionObserver;

ResizeObserver

window.ResizeObserver = class ResizeObserver {
  constructor(callback) {
    this.callback = callback;
  }
  observe() {}
  unobserve() {}
  disconnect() {}
};

localStorage

const localStorageMock = {
  getItem: jest.fn(),
  setItem: jest.fn(),
  removeItem: jest.fn(),
  clear: jest.fn(),
};

Object.defineProperty(window, 'localStorage', {
  value: localStorageMock,
});

window.location

delete window.location;
window.location = {
  href: 'http://localhost:3000',
  pathname: '/',
  search: '',
  hash: '',
  assign: jest.fn(),
  reload: jest.fn(),
  replace: jest.fn(),
};

Navigator.geolocation

Object.defineProperty(navigator, 'geolocation', {
  value: {
    getCurrentPosition: jest.fn((success) =>
      success({ coords: { latitude: 0, longitude: 0 } })
    ),
  },
});

Best Practices

  1. Mock at the boundary (API, not internal)
  2. Use MSW for API mocking
  3. Mock browser APIs in setup file
  4. Clear mocks between tests
  5. Don't mock what you don't own

Quiz

1. What is MSW (specific to mocking)?

Question 1 options

2. When should you mock?

Question 2 options

3. What is a common mistake when implementing Mocking in frontend applications?

Question 3 options

Flashcards

Question

What is mocking?

Answer

Creating fake implementations of functions or modules to isolate tests.

Question

When should you mock APIs?

Answer

When testing components that depend on external services, to isolate and control behavior.

Question

What is MSW?

Answer

Mock Service Worker - intercepts API requests for realistic testing without mocking fetch.

Question

What should you NOT mock?

Answer

Internal functions and modules you own - only mock external dependencies.

Revision Notes

Key Takeaways

  • 1. MSW is the recommended way to mock APIs
  • 2. Mock external dependencies, not internal code
  • 3. Mock browser APIs in setup files
  • 4. Clear mocks between tests
  • 5. Use partial mocks when needed

Interview Tips

  • Explain when and why to mock
  • Discuss MSW vs jest.mock for API mocking
  • Know how to mock browser APIs

Cheat Sheet

Mocking Cheat Sheet

MSW

http.get('/api/users', () => {
  return HttpResponse.json([{ id: 1 }]);
});

jest.mock

jest.mock('./api');
api.fetchUsers.mockResolvedValue(data);

Browser APIs

  • matchMedia
  • IntersectionObserver
  • localStorage
  • navigator.geolocation

Best Practices

  • Mock at boundaries
  • Clear mocks between tests
  • Don't mock what you don't own