Local storage и session storage

При тестировании React-компонентов с использованием библиотеки React Testing Library (RTL), часто возникает необходимость работать с данными, которые сохраняются в localStorage или sessionStorage. Это хранилища предоставляют веб-приложению возможность хранить данные на стороне клиента, и понимание их работы критически важно для написания корректных тестов, проверяющих функциональность компонентов, использующих эти API.

Обзор API

localStorage и sessionStorage — это два веб-API для хранения данных в браузере. Они отличаются по времени жизни данных:

  • localStorage сохраняет данные на неограниченный срок, пока не будет явным образом удалено.
  • sessionStorage сохраняет данные только в пределах одной сессии браузера. Как только вкладка или окно браузера закрываются, данные удаляются.

Оба API предоставляют методы для хранения, извлечения и удаления данных:

  • setItem(key, value) — сохраняет данные по ключу.
  • getItem(key) — извлекает данные по ключу.
  • removeItem(key) — удаляет данные по ключу.
  • clear() — удаляет все данные.

Эти хранилища особенно полезны при сохранении настроек пользователя, токенов аутентификации, состояния приложений и других данных, которые должны сохраняться между сессиями или вкладками.

Тестирование компонентов, использующих хранилища

При тестировании компонентов, которые взаимодействуют с localStorage или sessionStorage, важно учитывать, что в тестах необходимо управлять этим хранилищем. По умолчанию хранилища в тестах браузера могут быть пустыми или уже содержать данные. React Testing Library предоставляет методы, которые позволяют манипулировать этими хранилищами в тестах.

Подготовка окружения для тестов

Прежде чем тестировать компоненты, использующие localStorage или sessionStorage, нужно очистить их содержимое или подготовить начальные данные.

beforeEach(() => {
  localStorage.clear();
  sessionStorage.clear();
});

Этот код гарантирует, что хранилища будут очищены перед каждым тестом. Это важно для изоляции тестов и предотвращения “переноса” данных из одного теста в другой.

Запись и чтение данных из хранилища в тестах

Для того чтобы протестировать работу компонентов с localStorage или sessionStorage, можно использовать методы setItem и getItem. Например, если компонент зависит от определённых данных, которые хранятся в хранилище, можно установить эти данные перед рендерингом компонента.

it('сохраняет токен в localStorage и отображает данные пользователя', () => {
  const token = 'abc123';
  localStorage.setItem('authToken', token);

  render(<Profile />);

  expect(screen.getByText('Добро пожаловать, пользователь!')).toBeInTheDocument();
});

В этом примере мы заранее сохраняем токен в localStorage и проверяем, что компонент Profile правильно отображает данные после рендера.

Для sessionStorage процедура аналогична:

it('сохраняет данные сессии в sessionStorage и отображает их', () => {
  const sessionData = { userId: '123', sessionToken: 'xyz' };
  sessionStorage.setItem('sessionData', JSON.stringify(sessionData));

  render(<SessionInfo />);

  expect(screen.getByText('Session ID: 123')).toBeInTheDocument();
});

Моки для localStorage и sessionStorage

В случае, если необходимо более гибко контролировать поведение localStorage или sessionStorage, можно использовать моки. Например, при работе с Jest можно замокать эти API:

global.localStorage = {
  getItem: jest.fn(),
  setItem: jest.fn(),
  removeItem: jest.fn(),
  clear: jest.fn(),
};

Такой подход позволяет протестировать вызовы методов хранилища, не полагаясь на реальное хранилище браузера. Это также помогает предотвратить нежелательные побочные эффекты, такие как сохранение данных между тестами.

it('вызывается localStorage.setItem при сохранении данных', () => {
  const token = 'abc123';

  render(<Login />);

  fireEvent.click(screen.getByText('Войти'));

  expect(localStorage.setItem).toHaveBeenCalledWith('authToken', token);
});

Очистка и сброс данных после тестов

После завершения тестов необходимо очистить все данные из хранилища, чтобы не возникли конфликты с последующими тестами. Это особенно важно при работе с глобальными хранилищами, такими как localStorage или sessionStorage, которые могут сохранять данные между тестами.

afterEach(() => {
  localStorage.clear();
  sessionStorage.clear();
});

Эта операция помогает поддерживать чистоту тестового окружения и предотвращает ошибки, вызванные сохранением лишних данных.

Тестирование компонентов с асинхронной работой с хранилищами

Если компонент асинхронно взаимодействует с localStorage или sessionStorage, например, через API или асинхронные операции, тестирование требует особого подхода. В таких случаях важно дождаться завершения всех асинхронных операций перед проверкой состояния компонента.

it('обрабатывает данные из localStorage после загрузки', async () => {
  const userData = { name: 'Иван', age: 30 };
  localStorage.setItem('user', JSON.stringify(userData));

  render(<UserProfile />);

  await waitFor(() => {
    expect(screen.getByText('Имя: Иван')).toBeInTheDocument();
    expect(screen.getByText('Возраст: 30')).toBeInTheDocument();
  });
});

Здесь мы сначала устанавливаем данные в localStorage, затем рендерим компонент и используем waitFor для того, чтобы дождаться, пока асинхронная операция завершится и данные появятся в интерфейсе.

Подведение итогов

Работа с localStorage и sessionStorage в тестах React-компонентов с использованием React Testing Library требует внимательности к управлению состоянием хранилищ. Правильное очищение хранилищ перед и после тестов, использование моков для замены реальных вызовов методов хранилища и асинхронные ожидания — важные аспекты для стабильности тестов. С учётом этих факторов можно эффективно тестировать компоненты, взаимодействующие с веб-хранилищами.