Тестирование компонентов, работающих с внешними API, является важной частью разработки на React. React Testing Library (RTL) сама по себе фокусируется на тестировании UI через поведение пользователя, поэтому прямой работы с сетевыми запросами она не выполняет. Для проверки взаимодействия с REST и GraphQL endpoints используются методы мокирования сетевых запросов и асинхронного ожидания результатов.
Для тестирования компонентов, использующих REST API, чаще всего
применяются библиотеки jest и msw (Mock
Service Worker). Они позволяют перехватывать HTTP-запросы и возвращать
заранее определённые ответы, без необходимости реально обращаться к
серверу.
Пример мокирования с msw:
// handlers.js
import { rest } from 'msw';
export const handlers = [
rest.get('/api/users', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
])
);
}),
];
// test setup
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
const server = setupServer(...handlers);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
Компонент, который запрашивает пользователей:
import { useEffect, useState } from 'react';
function UsersList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(setUsers);
}, []);
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
export default UsersList;
Тест с RTL и msw:
import { render, screen, waitFor } from '@testing-library/react';
import UsersList from './UsersList';
test('отображает список пользователей', async () => {
render(<UsersList />);
await waitFor(() => {
expect(screen.getByText('Alice')).toBeInTheDocument();
expect(screen.getByText('Bob')).toBeInTheDocument();
});
});
Ключевые моменты:
waitFor используется для ожидания асинхронных
обновлений DOM.server.use для проверки ошибок.GraphQL требует немного другой стратегии, так как запросы идут через
единый endpoint и отличаются только структурой запроса. Для этого удобен
пакет msw с graphql обработчиком.
Пример обработки GraphQL запроса:
import { graphql } from 'msw';
export const handlers = [
graphql.query('GetUsers', (req, res, ctx) => {
return res(
ctx.data({
users: [
{ id: '1', name: 'Alice' },
{ id: '2', name: 'Bob' }
]
})
);
}),
];
Компонент с GraphQL (используется graphql-request):
import { useEffect, useState } from 'react';
import { request, gql } from 'graphql-request';
const GET_USERS = gql`
query GetUsers {
users {
id
name
}
}
`;
function UsersListGraphQL() {
const [users, setUsers] = useState([]);
useEffect(() => {
request('/graphql', GET_USERS).then(data => setUsers(data.users));
}, []);
return (
<ul>
{users.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
export default UsersListGraphQL;
Тестирование с RTL:
import { render, screen, waitFor } from '@testing-library/react';
import UsersListGraphQL from './UsersListGraphQL';
test('GraphQL: отображает список пользователей', async () => {
render(<UsersListGraphQL />);
await waitFor(() => {
expect(screen.getByText('Alice')).toBeInTheDocument();
expect(screen.getByText('Bob')).toBeInTheDocument();
});
});
Особенности тестирования GraphQL:
graphql.query или
graphql.mutation.ctx.errors.В обоих случаях необходимо учитывать асинхронность запросов. React Testing Library предоставляет утилиты:
waitFor — ожидает выполнения колбэка до тех пор, пока
не завершится асинхронное обновление DOM.findBy* — комбинирует поиск и ожидание элемента в
DOM.waitForElementToBeRemoved — полезно для проверки
загрузочных состояний.Пример проверки загрузки:
test('отображает индикатор загрузки и список пользователей', async () => {
render(<UsersList />);
expect(screen.getByText('Загрузка...')).toBeInTheDocument();
const alice = await screen.findByText('Alice');
expect(alice).toBeInTheDocument();
});
Тестирование API должно охватывать не только успешные ответы, но и ошибки:
server.use(
rest.get('/api/users', (req, res, ctx) => {
return res(ctx.status(500));
})
);
test('отображает сообщение об ошибке при сбое запроса', async () => {
render(<UsersList />);
const errorMessage = await screen.findByText('Ошибка загрузки пользователей');
expect(errorMessage).toBeInTheDocument();
});
Для GraphQL ошибки обрабатываются аналогично:
server.use(
graphql.query('GetUsers', (req, res, ctx) => {
return res(ctx.errors([{ message: 'Internal server error' }]));
})
);
server.resetHandlers()).fetch.msw вместо jest mocks:
Позволяет тестировать реальное взаимодействие с сетевым слоем.findBy и
waitFor упрощает работу с асинхронными
компонентами.Эти методы создают надёжную стратегию тестирования компонентов React, которые взаимодействуют как с REST, так и с GraphQL endpoints, обеспечивая стабильность и предсказуемость тестов без обращения к реальному серверу.