Изоляция тестов в Jest — это один из ключевых аспектов качественного тестирования, который позволяет удостовериться, что каждый тест выполняется в чистой, независимой среде. Это минимизирует вероятность ошибок, связанных с состоянием, которое может изменяться между тестами. В этой главе рассмотрим, как изолировать тесты, какие средства предоставляет Jest для этого и как правильно организовать тестовую среду.
Тесты должны быть независимыми. Изоляция необходима для того, чтобы изменения в одном тесте не влияли на другие. Когда тесты не изолированы, результат одного теста может зависеть от выполнения предыдущего. Это приводит к трудностям при отладке и снижает предсказуемость тестов.
Изоляция помогает:
beforeEach и afterEach для настройки и
очисткиJest предоставляет хуки beforeEach и
afterEach, которые позволяют настроить состояние перед
каждым тестом и очистить его после. Эти функции важны для изоляции, так
как они помогают гарантировать, что каждый тест начинается с чистого
состояния.
beforeEach:let counter;
beforeEach(() => {
counter = 0;
});
test('увеличивает счетчик', () => {
counter += 1;
expect(counter).toBe(1);
});
test('сбросит счетчик перед каждым тестом', () => {
expect(counter).toBe(0);
});
В этом примере beforeEach гарантирует, что переменная
counter будет сброшена перед каждым тестом, что исключает
влияние одного теста на другой.
afterEach:let db;
beforeEach(() => {
db = connectToDatabase();
});
afterEach(() => {
db.close();
});
test('должен сохранять данные в базу', () => {
db.save({ id: 1, name: 'test' });
expect(db.find(1)).toEqual({ id: 1, name: 'test' });
});
Здесь afterEach используется для закрытия соединения с
базой данных после каждого теста. Это важно для предотвращения утечек
памяти и других побочных эффектов.
beforeAll и afterAllИногда требуется выполнить настройку только один раз для всех тестов
в наборе, например, при подключении к базе данных или серверу. Для этого
можно использовать хуки beforeAll и afterAll,
которые выполняются один раз до начала всех тестов и после завершения
всех тестов в файле.
let server;
beforeAll(() => {
server = startServer();
});
afterAll(() => {
server.close();
});
test('тестирование с сервером', () => {
const response = fetchDataFromServer(server);
expect(response).toBe('success');
});
В этом случае сервер будет запущен только один раз перед всеми тестами, а не перед каждым из них. Это повышает производительность, когда настройка ресурсоемка, но при этом сохраняет изоляцию.
Для полной изоляции тестов важно мокуировать внешние зависимости.
Jest предоставляет встроенную поддержку моков с помощью функций
jest.mock(), jest.spyOn() и других. Это
позволяет заменить реальные зависимости на фиктивные, которые можно
контролировать и отслеживать.
// realModule.js
export const fetchData = () => {
return fetch('https://api.example.com/data')
.then(response => response.json());
};
// mockModule.js
jest.mock('./realModule', () => ({
fetchData: jest.fn(() => Promise.resolve({ data: 'mocked data' }))
}));
import { fetchData } from './realModule';
test('тест с мокированными данными', async () => {
const data = await fetchData();
expect(data).toEqual({ data: 'mocked data' });
});
В этом примере функция fetchData замещена на мок-версию,
которая всегда возвращает заранее определённые данные. Это позволяет
изолировать тесты от реальных внешних вызовов.
jest.resetAllMocks и
jest.clearAllMocksДля того чтобы убедиться, что моки не сохраняют своё состояние между
тестами, можно использовать функции jest.resetAllMocks() и
jest.clearAllMocks(). Они сбрасывают состояние всех моков,
шпионов и таймеров.
jest.resetAllMocks() — сбрасывает все моки и
шпионы.jest.clearAllMocks() — очищает вызовы и состояния
моков, не сбрасывая их реализацию.afterEach(() => {
jest.resetAllMocks();
});
test('проверка вызова функции', () => {
const mockFn = jest.fn();
mockFn();
expect(mockFn).toHaveBeenCalled();
});
Это гарантирует, что каждый тест будет работать с чистыми мокающими функциями, без влияния предыдущих тестов.
jest.resetModules()Когда тесты зависят от модулей, которые кэшируются в процессе
выполнения тестов, можно использовать jest.resetModules()
для сброса модуля, чтобы он загружался заново. Это полезно, если тесты
изменяют состояние или поведение импортированных модулей.
let myModule;
beforeEach(() => {
jest.resetModules();
myModule = require('./myModule');
});
test('проверка модуля', () => {
expect(myModule.someFunction()).toBe('default value');
});
Здесь jest.resetModules() сбрасывает кэш всех
импортированных модулей, чтобы каждый тест начинался с чистого
состояния.
Асинхронные тесты также требуют изоляции. В Jest для этого
используется поддержка async/await и методов, таких как
done или resolves. Эти механизмы позволяют
правильно изолировать асинхронные операции и гарантировать, что тесты не
будут зависеть от других.
test('асинхронная операция', async () => {
const data = await fetchDataFromServer();
expect(data).toEqual({ success: true });
});
При этом каждый тест начинается с чистого состояния благодаря хукам
beforeEach или beforeAll, что гарантирует
корректную работу всех асинхронных операций.
Изоляция тестов в Jest — это важный аспект написания стабильных и предсказуемых тестов. С помощью хуков для подготовки и очистки, моков, а также инструментов для сброса состояния можно гарантировать, что каждый тест будет независимым. Это позволяет повысить качество тестирования, упростить отладку и сделать код более поддерживаемым в долгосрочной перспективе.