Коллбеки и функции обратного вызова

Коллбеки (или функции обратного вызова) играют важную роль в JavaScript, включая контекст тестирования компонентов с использованием React Testing Library. Они используются для обработки асинхронных событий, пользовательского ввода или других реакций, которые должны происходить в ответ на действия, происходящие в приложении. В тестах React компоненты часто взаимодействуют с коллбеками, и важно знать, как их правильно тестировать и эмулировать.

Основы коллбеков в React

В React коллбеки часто передаются в компоненты в виде пропсов, и эти функции вызываются при изменении состояния или при выполнении действия пользователем. Например, это может быть обработчик события для кнопки, который изменяет состояние компонента:

const MyComponent = ({ onClick }) => {
  return <button onCl ick={onClick}>Click me</button>;
};

В данном примере коллбек onClick передается как пропс в компонент и вызывается, когда пользователь нажимает кнопку.

Тестирование коллбеков с React Testing Library

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

Мока функций коллбеков

Одним из наиболее часто используемых подходов в тестировании является мокирование (или подмена) коллбеков. Это позволяет проверить, был ли вызван тот или иной коллбек, и проверить, как компоненты взаимодействуют с переданными функциями.

Для этого React Testing Library предоставляет полезные утилиты, такие как jest.fn(), чтобы создать «мок» функции, которая будет отслеживать вызовы и их параметры.

Пример:

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

test('calls onClick callback when button is clicked', () => {
  const mockOnClick = jest.fn(); // Мокаем коллбек
  const { getByText } = render(<MyComponent onCl ick={mockOnClick} />);
  
  fireEvent.click(getByText('Click me')); // Эмулируем клик по кнопке
  
  expect(mockOnClick).toHaveBeenCalledTimes(1); // Проверяем, что коллбек был вызван один раз
});

В этом примере создается мок-функция mockOnClick, которая передается в компонент через пропс onClick. В тесте вызывается клик по кнопке, и проверяется, был ли вызван коллбек. Используя jest.fn(), можно дополнительно проверить параметры, с которыми был вызван коллбек, а также количество вызовов.

Проверка аргументов коллбеков

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

Пример:

test('calls onChange with correct argument', () => {
  const mockOnChange = jest.fn();
  const { getByLabelText } = render(<MyComponent onCha nge={mockOnChange} />);
  
  fireEvent.change(getByLabelText('Input field'), { target: { value: 'Hello' } });

  expect(mockOnChange).toHaveBeenCalledWith('Hello');
});

Здесь проверяется, что функция обратного вызова onChange была вызвана с правильным значением, переданным через событие изменения.

Асинхронные коллбеки

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

Для таких случаев React Testing Library и Jest предоставляют утилиты для работы с асинхронными тестами. Основной метод для работы с асинхронными событиями — это await с функциями, которые возвращают промисы.

Пример асинхронного коллбека:

const MyComponent = ({ onAsyncClick }) => {
  const handleClick = async () => {
    const result = await fetchData();
    onAsyncClick(result);
  };

  return <button onCl ick={handleClick}>Fetch Data</button>;
};

test('calls onAsyncClick after fetching data', async () => {
  const mockOnAsyncClick = jest.fn();
  const { getByText } = render(<MyComponent onAsyncCl ick={mockOnAsyncClick} />);

  fireEvent.click(getByText('Fetch Data'));

  await waitFor(() => expect(mockOnAsyncClick).toHaveBeenCalledWith('data'));
});

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

Дельта состояния и отложенные коллбеки

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

Пример:

const MyComponent = ({ onUpdate }) => {
  const [value, setValue] = useState('');

  const handleClick = () => {
    setValue('Updated!');
    onUpdate();
  };

  return (
    <div>
      <button onCl ick={handleClick}>Update</button>
      <p>{value}</p>
    </div>
  );
};

test('updates value and calls onUpdate callback', () => {
  const mockOnUpdate = jest.fn();
  const { getByText, getByRole } = render(<MyComponent onUPDATE={mockOnUpdate} />);

  fireEvent.click(getByRole('button', { name: /update/i }));

  expect(getByText('Updated!')).toBeInTheDocument();
  expect(mockOnUpdate).toHaveBeenCalledTimes(1);
});

В этом тесте проверяется, что после клика по кнопке значение обновляется, а коллбек onUpdate вызывается.

Заключение

Тестирование коллбеков является важной частью тестирования взаимодействий в React компонентах. Использование мок-функций, проверка аргументов и асинхронных вызовов помогает обеспечить корректность работы компонентов в реальных условиях. Основные принципы, такие как проверка количества вызовов и правильных значений, являются важными инструментами для поддержания надежности и предсказуемости поведения интерфейсов.