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
- Mock at the boundary (API, not internal)
- Use MSW for API mocking
- Mock browser APIs in setup file
- Clear mocks between tests
- Don't mock what you don't own
Practice Problems
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 neededWrite 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 testsOptimize 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 analysisQuiz
1. What is MSW?
2. When should you mock?
3. What is the primary purpose of Mocking?
4. What is a common mistake when implementing Mocking?
Flashcards
Question
What is mocking?
Click to reveal answer
Answer
Creating fake implementations of functions or modules to isolate tests.
Question
When should you mock APIs?
Click to reveal answer
Answer
When testing components that depend on external services, to isolate and control behavior.
Question
What is MSW?
Click to reveal answer
Answer
Mock Service Worker - intercepts API requests for realistic testing without mocking fetch.
Question
What should you NOT mock?
Click to reveal answer
Answer
Internal functions and modules you own - only mock external dependencies.
Question
What is Mocking?
Click to reveal answer
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