Тестирование error состояний

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

Обработка ошибок в React-компонентах

Для начала важно понять, как обрабатывать ошибки в компонентах React. В большинстве случаев ошибки обрабатываются с помощью состояния (state) компонента, которое может отражать успешность или неудачу операции. Также часто для обработки ошибок используются компоненты типа ErrorBoundary, которые перехватывают ошибки на уровне компонентов.

Пример обработки ошибки через состояние:

import React, { useState, useEffect } from 'react';

const DataFetcher = () => {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch('/api/data')
      .then((response) => response.json())
      .then((result) => setData(result))
      .catch((err) => setError('Не удалось загрузить данные'));
  }, []);

  if (error) {
    return <div role="alert">{error}</div>;
  }

  return data ? <div>{JSON.stringify(data)}</div> : <div>Загрузка...</div>;
};

В этом примере компонента DataFetcher использует состояния для хранения как загруженных данных, так и возможных ошибок. Если возникает ошибка при загрузке данных, отображается сообщение об ошибке. Важно, что компонент использует роль alert для сообщения об ошибке, что помогает с доступностью и улучшает тестируемость компонента.

Основные подходы к тестированию error состояний

В React Testing Library можно использовать несколько подходов для тестирования error состояний компонентов. Ключевыми аспектами являются:

  • Имитация ошибки: необходимо симулировать ошибки, чтобы проверить, как компонент реагирует на них.
  • Проверка отображения ошибок: важно убедиться, что компоненты правильно отображают сообщения об ошибках.
  • Тестирование доступности: при отображении ошибок важно убедиться, что компоненты соблюдают лучшие практики доступности, такие как использование ролей и атрибутов.

Использование jest.mock для имитации ошибок

Для имитации ошибок в тестах часто используется метод jest.mock, который позволяет подменять реальные вызовы, например, запросы к серверу, на заранее подготовленные ошибки.

Пример теста с имитацией ошибки при загрузке данных:

import { render, screen, waitFor } from '@testing-library/react';
import DataFetcher from './DataFetcher';

// Мокаем функцию fetch, чтобы она всегда выбрасывала ошибку
jest.mock('node-fetch', () => jest.fn(() => Promise.reject(new Error('Network error'))));

test('отображает сообщение об ошибке при неудачной загрузке данных', async () => {
  render(<DataFetcher />);

  await waitFor(() => screen.getByRole('alert'));

  expect(screen.getByRole('alert')).toHaveTextContent('Не удалось загрузить данные');
});

В этом примере с помощью jest.mock мы подменяем стандартный fetch на версию, которая всегда возвращает ошибку. После рендера компонента мы ожидаем, что на экране появится сообщение об ошибке.

Тестирование асинхронных ошибок

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

Пример:

import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import UserProfile from './UserProfile';

test('отображает ошибку при неудачной загрузке профиля', async () => {
  render(<UserProfile />);

  // Мокируем запрос, который возвращает ошибку
  global.fetch = jest.fn().mockRejectedValueOnce(new Error('Ошибка при загрузке профиля'));

  fireEvent.click(screen.getByText(/Загрузить профиль/i));

  await waitFor(() => screen.getByRole('alert'));

  expect(screen.getByRole('alert')).toHaveTextContent('Ошибка при загрузке профиля');
});

Здесь мы сначала мокируем ошибку с помощью mockRejectedValueOnce, затем симулируем клик по кнопке, инициирующей загрузку данных. После выполнения асинхронной операции с помощью waitFor проверяется, что на экране появилось сообщение об ошибке.

Тестирование отображения ошибок при использовании компонентов типа ErrorBoundary

Если в проекте используется компонент ErrorBoundary для перехвата ошибок, то тестирование будет немного отличаться. Компонент ErrorBoundary позволяет обрабатывать ошибки, возникающие в дочерних компонентах.

Пример компонента ErrorBoundary:

import React, { Component } from 'react';

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    console.error('Error caught by ErrorBoundary:', error, info);
  }

  render() {
    if (this.state.hasError) {
      return <div role="alert">Что-то пошло не так</div>;
    }

    return this.props.children;
  }
}

Для тестирования можно имитировать ошибку внутри дочернего компонента и убедиться, что ErrorBoundary правильно обработает эту ошибку.

Пример теста:

import { render, screen } from '@testing-library/react';
import ErrorBoundary from './ErrorBoundary';

test('показывает ошибку при возникновении ошибки в дочернем компоненте', () => {
  const BrokenComponent = () => {
    throw new Error('Ошибка в компоненте');
  };

  render(
    <ErrorBoundary>
      <BrokenComponent />
    </ErrorBoundary>
  );

  expect(screen.getByRole('alert')).toHaveTextContent('Что-то пошло не так');
});

Этот тест проверяет, что когда в компоненте BrokenComponent возникает ошибка, она перехватывается ErrorBoundary, который отображает сообщение об ошибке.

Доступность при тестировании error состояний

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

Пример с использованием роли alert:

test('сообщение об ошибке доступно для экранных читалок', () => {
  render(<DataFetcher />);

  // Симулируем ошибку
  fireEvent.click(screen.getByText('Загрузить данные'));

  expect(screen.getByRole('alert')).toHaveTextContent('Не удалось загрузить данные');
});

В этом тесте проверяется, что при возникновении ошибки компонент выводит сообщение с ролью alert, что позволяет улучшить доступность приложения для пользователей с особыми потребностями.

Подведение итогов тестирования error состояний

Тестирование error состояний в React с использованием React Testing Library заключается в том, чтобы правильно симулировать ошибки, проверять корректное отображение сообщений об ошибках и обеспечивать доступность этих сообщений для всех пользователей. Важными инструментами для этого являются jest.mock, асинхронные ожидания через waitFor и использование правильных ролей для элементов ошибок. Properly handling and testing error states ensures better stability and user experience in real-world applications.