В процессе тестирования компонентов React часто возникает
необходимость взаимодействовать с браузерными встроенными API. Два самых
распространённых примера — это localStorage и
fetch. Для написания надёжных и изолированных тестов важно
правильно их мокировать, чтобы избежать зависимости от состояния
браузера или сетевых запросов.
localStoragelocalStorage используется для хранения данных на стороне
клиента. В тестах прямое обращение к нему может приводить к
непредсказуемым результатам, поэтому применяют мокирование.
localStoragegetItem(key) — возвращает значение по ключу.setItem(key, value) — сохраняет значение по ключу.removeItem(key) — удаляет элемент.clear() — очищает весь storage.localStorage в JestbeforeEach(() => {
const localStorageMock = (() => {
let store = {};
return {
getItem: (key) => store[key] || null,
setItem: (key, value) => {
store[key] = value.toString();
},
removeItem: (key) => {
delete store[key];
},
clear: () => {
store = {};
},
};
})();
Object.defineProperty(window, 'localStorage', {
value: localStorageMock,
});
});
test('сохраняет данные в localStorage', () => {
localStorage.setItem('token', '12345');
expect(localStorage.getItem('token')).toBe('12345');
});
Ключевые моменты:
store для хранения
значений.getItem и setItem эмулируют
стандартное поведение localStorage.Object.defineProperty используется для переопределения
глобального window.localStorage.Иногда требуется мокировать localStorage только для
одного теста:
test('удаляет элемент из localStorage', () => {
const removeItemMock = jest.fn();
Object.defineProperty(window, 'localStorage', {
value: { removeItem: removeItemMock },
});
localStorage.removeItem('key');
expect(removeItemMock).toHaveBeenCalledWith('key');
});
Такой подход удобен для проверки вызовов без фактического хранения данных.
fetchfetch используется для сетевых запросов. В тестах важно
избежать реальных HTTP-запросов, чтобы тесты были быстрыми и
детерминированными.
В Jest можно заменить fetch на мок-функцию:
beforeEach(() => {
global.fetch = jest.fn();
});
afterEach(() => {
jest.resetAllMocks();
});
test('получает данные с API', async () => {
const fakeData = { id: 1, name: 'Test' };
fetch.mockResolvedValue({
ok: true,
json: async () => fakeData,
});
const response = await fetch('/api/data');
const data = await response.json();
expect(fetch).toHaveBeenCalledWith('/api/data');
expect(data).toEqual(fakeData);
});
Особенности:
mockResolvedValue возвращает объект, имитирующий
успешный Response.json также мокируется для возврата нужных
данных.fetch с помощью
toHaveBeenCalledWith.test('обрабатывает ошибку fetch', async () => {
fetch.mockRejectedValue(new Error('Network error'));
await expect(fetch('/api/data')).rejects.toThrow('Network error');
});
Такой подход позволяет тестировать обработку ошибок без реальных сетевых сбоев.
mswДля сложных сценариев, когда требуется более реалистичная имитация сетевых запросов, используется библиотека Mock Service Worker (MSW).
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('/api/data', (req, res, ctx) => {
return res(ctx.json({ id: 1, name: 'Test' }));
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test('получает данные через MSW', async () => {
const response = await fetch('/api/data');
const data = await response.json();
expect(data).toEqual({ id: 1, name: 'Test' });
});
Преимущества MSW:
localStorage или реальных сетевых запросов.jest.resetAllMocks() или jest.clearAllMocks()
для предотвращения влияния одного теста на другой.jest.fn(), для интеграционных — msw
даёт более реалистичное поведение.Мокирование встроенных API — ключевой элемент качественного тестирования React-приложений, позволяющий создавать быстрые, надёжные и воспроизводимые тесты без зависимости от браузерного состояния или сети.