JavaRush /Курсы /Модуль 3: React /Тестирование ключевых компонентов

Тестирование ключевых компонентов

Модуль 3: React
19 уровень , 8 лекция
Открыта

Контроль качества

Мы реализовали многое: от авторизации через JWT и работы с транзакциями — до оптимизаций производительности и темизации интерфейса. Чтобы убедиться, что наше приложение работает корректно при изменениях, мы подключим юнит- и интеграционные тесты.

Сегодня мы:

  • установим всё необходимое для тестирования;
  • напишем тесты для компонентов TransactionList, AddTransactionForm, LoginPage;
  • проверим Redux-хранилище;
  • и протестируем асинхронную загрузку транзакций через API.

Установка инструментов

Для начала установим зависимости:

npm install --save-dev jest @testing-library/react @testing-library/jest-dom @testing-library/user-event ts-jest jest-environment-jsdom

Инициализируем конфигурацию для TypeScript:

npx ts-jest config:init

Обновим package.json:

"scripts": {
  "test": "jest"
}

Тестирование компонента TransactionList

Этот компонент выводит список транзакций, полученных через React Query.

Файл src/components/TransactionList.tsx

Цель теста

Проверим, что:

  • компонент корректно отображает список;
  • отображается индикатор загрузки;
  • происходит корректный вывод при получении данных.

Тест

Файл src/components/TransactionList.test.tsx

import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import TransactionList from './TransactionList';
import * as transactionService from '../services/transactionService';

// Мокаем fetchTransactions
jest.mock('../services/transactionService');

const mockedFetch = transactionService.fetchTransactions as jest.Mock;

const mockTransactions = [
  {
    id: 1,
    category: 'Еда',
    amount: 25,
    date: '2024-01-01T12:00:00Z',
    type: 'expense',
  },
  {
    id: 2,
    category: 'Зарплата',
    amount: 1000,
    date: '2024-01-05T12:00:00Z',
    type: 'income',
  },
];

const renderWithQuery = () =>
  render(
    <QueryClientProvider client={new QueryClient()}>
      <TransactionList />
    </QueryClientProvider>
  );

test('отображает список транзакций', async () => {
  mockedFetch.mockResolvedValueOnce(mockTransactions);

  renderWithQuery();

  expect(screen.getByText(/Загрузка транзакций/)).toBeInTheDocument();

  await waitFor(() => {
    expect(screen.getByText(/Еда/)).toBeInTheDocument();
    expect(screen.getByText(/Зарплата/)).toBeInTheDocument();
  });
});

Тестирование компонента AddTransactionForm

Этот компонент позволяет пользователю добавить транзакцию, и обновляет кэш через useMutation.

Файл src/components/AddTransactionForm.tsx

Цель теста

Проверим, что:

  • пользователь может ввести сумму и категорию;
  • при отправке вызывается мутация;
  • поля очищаются после отправки.

Файл src/components/AddTransactionForm.test.tsx

import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import AddTransactionForm from './AddTransactionForm';
import * as service from '../services/transactionService';

jest.mock('../services/transactionService');
const mockedCreateTransaction = service.createTransaction as jest.Mock;

const renderForm = () =>
  render(
    <QueryClientProvider client={new QueryClient()}>
      <AddTransactionForm />
    </QueryClientProvider>
  );

test('добавляет транзакцию и очищает форму', async () => {
  mockedCreateTransaction.mockResolvedValueOnce({ id: 3 });

  renderForm();

  const amountInput = screen.getByPlaceholderText('Сумма');
  const categoryInput = screen.getByPlaceholderText('Категория');
  const addButton = screen.getByText('Добавить');

  await userEvent.type(amountInput, '50');
  await userEvent.type(categoryInput, 'Кафе');
  await userEvent.click(addButton);

  expect(mockedCreateTransaction).toHaveBeenCalledWith(
    expect.objectContaining({ amount: 50, category: 'Кафе' })
  );

  expect(amountInput).toHaveValue('');
  expect(categoryInput).toHaveValue('');
});

Тестирование страницы LoginPage

Файл src/pages/LoginPage.tsx

Компонент отвечает за вход пользователя и сохранение токена.

Цель теста

Проверим, что:

  • отображаются поля ввода;
  • при успешном логине вызывается AuthService.login;
  • навигация срабатывает.

Файл src/pages/LoginPage.test.tsx

import React from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import LoginPage from './LoginPage';
import * as authService from '../services/AuthService';
import { useAuth } from '../hooks/useAuth';

jest.mock('../services/AuthService');
jest.mock('../hooks/useAuth');

const mockedLogin = authService.login as jest.Mock;
const mockedUseAuth = useAuth as jest.Mock;

test('выполняет вход и вызывает login()', async () => {
  mockedLogin.mockResolvedValueOnce('mock-token');
  const loginMock = jest.fn();
  mockedUseAuth.mockReturnValue({ login: loginMock });

  render(<LoginPage />);

  await userEvent.type(screen.getByPlaceholderText(/Email/), 'user@example.com');
  await userEvent.type(screen.getByPlaceholderText(/Пароль/), 'password');
  await userEvent.click(screen.getByRole('button', { name: /Войти/i }));

  expect(mockedLogin).toHaveBeenCalledWith('user@example.com', 'password');
  expect(loginMock).toHaveBeenCalledWith('mock-token');
});

Тестирование Redux-хранилища

Файлы src/store.ts, src/redux/transactionSlice.ts

Проверим, что редьюсер transactionSlice корректно обрабатывает действия.

Файл src/redux/transactionSlice.test.ts

import reducer, { addTransaction } from './transactionSlice';

test('добавляет транзакцию', () => {
  const initialState = { transactions: [] };

  const action = addTransaction({
    id: 1,
    category: 'Продукты',
    amount: 120,
    date: new Date().toISOString(),
    type: 'expense',
  });

  const result = reducer(initialState, action);

  expect(result.transactions).toHaveLength(1);
  expect(result.transactions[0].category).toBe('Продукты');
});

Тестирование API: transactionService

Многие наши компоненты используют сервис fetchTransactions, createTransaction, и важно проверить, что они работают корректно.

Файл src/services/transactionService.test.ts

import axios from 'axios';
import { fetchTransactions } from './transactionService';

jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

test('получает список транзакций', async () => {
  const mockData = [{ id: 1, category: 'Еда', amount: 100 }];
  mockedAxios.get.mockResolvedValue({ data: mockData });

  const result = await fetchTransactions();

  expect(mockedAxios.get).toHaveBeenCalledWith('/transactions');
  expect(result).toEqual(mockData);
});

Надеюсь, теперь тестирование кажется чуть менее пугающим! Впереди вас ждёт ещё больше интересного об инструментах и методологиях, которые сделают ваш код непотопляемым.

2
Задача
Модуль 3: React, 19 уровень, 8 лекция
Недоступна
Тестирование состояния компонента
Тестирование состояния компонента
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ