Skip to content
intermediatePhase 40 · Testing

Mocking

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

30m
0 problems
Topic Progress0%

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

Practice Problems

0/3solved
Build Mocking Component

Create a reusable React component implementing Mocking. Include proper state management and accessibility.

Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where needed
Mocking Testing

Write unit and integration tests for Mocking using React Testing Library.

Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility tests
Mocking Performance

Optimize Mocking for performance. Consider memoization, code splitting, and bundle size.

Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysis

Quiz

1. What is MSW?

Question 1 options

2. When should you mock?

Question 2 options

3. What is the primary purpose of Mocking?

Question 3 options

4. What is a common mistake when implementing Mocking?

Question 4 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.

Question

What is Mocking?

Answer

Mocking is a key concept in frontend development.

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