Как провести тестирование функции в компоненте React, используя Jest и React Testing Library?

Как правильно протестировать функцию <b>handleChangeRoadCosts</b> с точки зрения проверки логики преобразования объекта? Стоит ли вынести её в отдельный файл и использовать Jest для тестирования, или лучше создать пользовательский хук, замокать его и тестировать таким образом?
  • 10 октября 2024 г. 17:14
Ответы на вопрос 3
Тестирование функции `handleChangeRoadCosts` в компоненте React можно провести несколькими способами, в зависимости от сложности логики и ваших предпочтений. Давайте рассмотрим несколько подходов.

### 1. Тестирование внутри компонента с использованием Jest и React Testing Library

Если `handleChangeRoadCosts` является частью компонента и используется только в нем, то вы можете протестировать ее косвенно через тестирование поведения компонента. Для этого вам нужно будет написать тесты, которые проверяют, как компонент реагирует на события, вызывающие `handleChangeRoadCosts`.

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

```javascript
import { render, screen, fireEvent } from '@testing-library/react';
import YourComponent from './YourComponent';

test('should update road costs correctly on input change', () => {
    render(<YourComponent />);
    
    const input = screen.getByLabelText('Road Costs'); // предположим, что в компоненте есть input с лейблом "Road Costs"
    fireEvent.change(input, { target: { value: '100' } });

    expect(screen.getByText(/road costs updated to/i)).toBeInTheDocument(); // или любая другая проверка
});
```

### 2. Вынос функции в отдельный файл

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

Например:

```javascript
// roadCostsHelper.js
export const handleChangeRoadCosts = (costs) => {
    // логика преобразования
};
```

Тестирование функции:

```javascript
import { handleChangeRoadCosts } from './roadCostsHelper';

test('should transform road costs correctly', () => {
    const input = { /* ... */ }; // входные данные
    const expectedOutput = { /* ... */ }; // ожидаемый результат

    const result = handleChangeRoadCosts(input);
    expect(result).toEqual(expectedOutput);
});
```

### 3. Создание пользовательского хука

Если `handleChangeRoadCosts` является логикой управления состоянием, то имеет смысл создать пользовательский хук. Затем вы можете замокать его в тестах и протестировать поведение компонента.

Создание хука:

```javascript
// useRoadCosts.js
import { useState } from 'react';

export const useRoadCosts = () => {
    const [costs, setCosts] = useState(/* начальное состояние */);
    
    const handleChangeRoadCosts = (newCosts) => {
        // логика преобразования
        setCosts(transformedCosts);
    };

    return [costs, handleChangeRoadCosts];
};
```

Тестирование хука с использованием библиотек `@testing-library/react-hooks` или аналогичных:

```javascript
import { renderHook, act } from '@testing-library/react-hooks';
import { useRoadCosts } from './useRoadCosts';

test('should update road costs correctly', () => {
    const { result } = renderHook(() => useRoadCosts());

    act(() => {
        result.current[1]({ /* новые расходы */ });
    });

    expect(result.current[0]).toEqual({ /* ожидаемые расходы */ });
});
```

### Заключение

Какой из подходов выбрать, зависит от конкретного случая:

- Если `handleChangeRoadCosts` простая функция и используется только в одном компоненте, тогда имеет смысл тестировать её индиректно через тесты компонента.
- Если функция сложная и будет переиспользоваться, лучше вынести ее в отдельный файл и протестировать независимо.
- Если функция управляет состоянием, создайте пользовательский хук и протестируйте его отдельно.

Используйте тот подход, который лучше всего соответствует архитектуре вашего приложения.
Если там хитрая логика, то лучше вынести в отдельную функцию и к ней наколбасить тестов через чистый jest. Эта функция должна получать все данные, с которыми работает, через параметр. Например, твой handleChangeRoadCosts сейчас берет некую data из замыкания, а надо это передавать доп. параметром. Тогда ты легко напишешь тесты. 

В самом компоненте остаётся обезжиренный вариант, не требующий тестов:
import {myFunc} from '....';

........
const handleChangeRoadCosts = (id: number, keyVal: string, val: string) => {
    return myFunc(id, keyVal, val, data);
};
Если там хитрая логика, то лучше вынести в отдельную функцию и к ней наколбасить тестов через чистый jest. Эта функция должна получать все данные, с которыми работает, через параметр. Например, твой handleChangeRoadCosts сейчас берет некую data из замыкания, а надо это передавать доп. параметром. Тогда ты легко напишешь тесты. 

В самом компоненте остаётся обезжиренный вариант, не требующий тестов:
import {myFunc} from '....';

........
const handleChangeRoadCosts = (id: number, keyVal: string, val: string) => {
    return myFunc(id, keyVal, val, data);
};
Похожие вопросы