Тестирование с Jest - практическое руководство для JavaScript и TypeScript

05 марта 2026
Автор

Олег Марков

Введение

Jest сегодня один из самых популярных фреймворков для тестирования JavaScript и TypeScript кода. Его используют в проектах на React, Node.js, Next.js и во множестве других экосистем. Он позволяет быстро писать понятные автотесты, запускать их параллельно, собирать покрытие и получать наглядные отчеты.

Здесь вы увидите, как на практике использовать Jest для модульного и частично интеграционного тестирования. Мы последовательно разберем установку, конфигурацию, написание тестов, работу с асинхронностью, мокирование, снапшоты и специфические сценарии вроде тестирования таймеров или работы с файловой системой.

Я буду ориентироваться на практику: показывать код, разбирать типичные ситуации и объяснять, почему Jest ведет себя именно так. Вы сможете шаг за шагом настроить тестирование в реальном проекте, а не просто увидеть набор изолированных примеров.

Установка и базовая настройка Jest

Установка в проект JavaScript

Для начала вам нужен проект с Node.js и npm или yarn. Допустим, у вас уже есть package.json. Тогда установка выглядит так:

# Установка Jest как dev-зависимости
npm install --save-dev jest
# или
yarn add --dev jest

После установки вам нужно добавить скрипт для запуска тестов в package.json:

{
  "scripts": {
    // Команда для запуска всех тестов
    "test": "jest"
  }
}

Теперь вы можете запустить тесты командой:

npm test
# или
yarn test

Если тестов еще нет, Jest просто скажет, что ничего не найдено.

Подключение Jest в TypeScript-проект

Для TypeScript вам понадобится дополнительно ts-jest и типы Jest:

npm install --save-dev jest ts-jest @types/jest typescript
# или
yarn add --dev jest ts-jest @types/jest typescript

Дальше инициализируем ts-jest, чтобы создать конфигурацию:

npx ts-jest config:init

Эта команда создаст файл jest.config.js с базовыми настройками для TypeScript. Он может выглядеть так:

// jest.config.js
module.exports = {
  // Говорим Jest, что будем использовать ts-jest как трансформер для TypeScript
  preset: 'ts-jest',
  // Указываем среду выполнения тестов - по умолчанию это jsdom
  testEnvironment: 'node'
};

Здесь я использую testEnvironment: 'node', если вы тестируете backend-код. Для фронтенда (например, React) чаще оставляют jsdom.

В package.json скрипт будет таким же:

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

Организация файлов тестов

Где хранить тесты

У Jest есть несколько стандартных паттернов поиска тестов. По умолчанию он будет искать:

  • файлы, находящиеся в папках __tests__
  • файлы с суффиксом .test.js или .spec.js
  • то же самое для TypeScript — .test.ts и .spec.ts

Один из популярных вариантов — держать тесты рядом с кодом:

  • src/math/add.ts
  • src/math/add.test.ts

Либо использовать отдельную структуру:

  • src/add.ts
  • __tests__/add.test.ts

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

Простейший тестовый файл

Создадим простой модуль и напишем к нему тест. Смотрите, я покажу вам, как это выглядит по шагам.

Файл src/sum.js:

// src/sum.js

// Простая функция сложения двух чисел
function sum(a, b) {
  return a + b;
}

// Экспортируем функцию, чтобы она была доступна в тестах
module.exports = sum;

Теперь создадим тест src/sum.test.js:

// src/sum.test.js

// Импортируем тестируемую функцию
const sum = require('./sum');

// Описываем группу тестов для функции sum
describe('sum', () => {
  // Описываем конкретный тестовый случай
  test('складывает два положительных числа', () => {
    // Выполняем функцию и сохраняем результат
    const result = sum(2, 3);

    // Проверяем, что результат равен ожидаемому значению
    expect(result).toBe(5);
  });

  test('корректно работает с отрицательными числами', () => {
    const result = sum(-2, -3);
    expect(result).toBe(-5);
  });
});

Теперь вы можете запустить:

npm test

Jest найдет файл sum.test.js, выполнит тесты и покажет вам, сколько прошло и сколько упало.

Основные функции и структура тестов

describe, test и it

В Jest базовые строительные блоки такие:

  • describe — группировка тестов
  • test или it — конкретный тест
  • expect — проверка ожидаемого результата

Давайте разберемся на примере:

// user.test.js

// Описываем группу тестов для сущности "Пользователь"
describe('User entity', () => {
  // Конкретный тест - проверка отображения полной информации
  test('формирует полное имя пользователя', () => {
    const user = { firstName: 'Иван', lastName: 'Иванов' };

    // Здесь мы проверяем, что строка собрана правильно
    const fullName = `${user.firstName} ${user.lastName}`;

    expect(fullName).toBe('Иван Иванов');
  });

  // Альтернативный синтаксис - it вместо test
  it('поддерживает пользователей без фамилии', () => {
    const user = { firstName: 'Иван' };

    // Используем логическое ИЛИ, чтобы подставить пустую строку, если фамилии нет
    const fullName = `${user.firstName} ${user.lastName || ''}`.trim();

    expect(fullName).toBe('Иван');
  });
});

Функции test и it полностью эквивалентны, это просто вопрос стиля.

Структура ассертов с expect

Функция expect принимает фактическое значение и возвращает объект с матчерами (matcher), которые позволяют описать ожидаемое поведение.

Примеры распространенных матчеров:

test('основные матчеры', () => {
  const value = 2 + 2;

  // Проверка строгого равенства
  expect(value).toBe(4);

  // Проверка нестрогого равенства (по сути те же semantics, но для NaN используется специальная логика)
  expect(value).toEqual(4);

  // Проверка булевых выражений
  expect(value).toBeGreaterThan(3);
  expect(value).toBeGreaterThanOrEqual(4);
  expect(value).toBeLessThan(5);
  expect(value).toBeLessThanOrEqual(4);
});

test('проверка объектов и массивов', () => {
  const user = { name: 'Иван', age: 30 };
  const result = ['a', 'b', 'c'];

  // Проверка глубокого равенства объектов
  expect(user).toEqual({ name: 'Иван', age: 30 });

  // Проверка что объект содержит подмножество свойств
  expect(user).toMatchObject({ name: 'Иван' });

  // Проверка содержания элемента в массиве
  expect(result).toContain('b');
});

Смотрите, здесь важно понимать разницу между toBe и toEqual:

  • toBe использует сравнение по ссылке (для объектов) и по значению (для примитивов)
  • toEqual делает глубокое сравнение структур

Для объектов почти всегда используют toEqual.

Проверка ошибок и исключений

Покажу вам, как проверять, что код выбрасывает ошибку:

// errors.test.js

// Функция, которая выбрасывает ошибку при недопустимом значении
function divide(a, b) {
  if (b === 0) {
    throw new Error('Деление на ноль невозможно');
  }
  return a / b;
}

test('деление на ноль выбрасывает ошибку', () => {
  // Оборачиваем вызов функции в анонимную функцию
  // Jest ожидает именно функцию, а не результат ее вызова
  const call = () => divide(10, 0);

  // Проверяем, что при вызове выбрасывается ошибка
  expect(call).toThrow(Error);

  // Можно проверить конкретное сообщение
  expect(call).toThrow('Деление на ноль невозможно');
});

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

Тестирование асинхронного кода

Асинхронность часто вызывает вопросы, поэтому остановимся на этом подробнее.

Тестирование Promise с async/await

Предположим, у вас есть функция, которая возвращает Promise:

// fetchUser.js

// Имитация асинхронного запроса за данными пользователя
function fetchUser(id) {
  return new Promise((resolve) => {
    setTimeout(() => {
      // Здесь мы возвращаем объект пользователя через 100 мс
      resolve({ id, name: 'Иван' });
    }, 100);
  });
}

module.exports = fetchUser;

Теперь вы увидите, как это выглядит в тесте с async/await:

// fetchUser.test.js
const fetchUser = require('./fetchUser');

test('получение пользователя по id - async/await', async () => {
  // Ждем выполнения функции fetchUser
  const user = await fetchUser(1);

  // Проверяем, что пришли нужные данные
  expect(user).toEqual({ id: 1, name: 'Иван' });
});

Jest понимает, что если вы вернули из теста Promise (а async-функция всегда возвращает Promise), то нужно дождаться его завершения перед тем, как считать тест выполненным.

Тестирование отказов (reject) у Promise

Давайте посмотрим, как тестировать ошибки в Promise:

// fetchUserWithError.js

// Функция, которая иногда выбрасывает ошибку (reject)
function fetchUserWithError(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id <= 0) {
        // Если id некорректный, отклоняем промис
        reject(new Error('Некорректный идентификатор пользователя'));
      } else {
        resolve({ id, name: 'Иван' });
      }
    }, 50);
  });
}

module.exports = fetchUserWithError;

Теперь тест:

// fetchUserWithError.test.js
const fetchUserWithError = require('./fetchUserWithError');

test('ошибка при некорректном id - async/await', async () => {
  // Используем конструкцию rejects для проверки отклонения промиса
  await expect(fetchUserWithError(0)).rejects.toThrow(
    'Некорректный идентификатор пользователя'
  );
});

test('успешное выполнение при корректном id', async () => {
  // Используем конструкцию resolves для проверки успешного резолва промиса
  await expect(fetchUserWithError(1)).resolves.toEqual({ id: 1, name: 'Иван' });
});

Обратите внимание: в асинхронных тестах важно возвращать Promise (или использовать async/await и await перед expect(...).rejects/ resolves), иначе Jest может завершить тест раньше времени.

Колбэки (callback-style)

В старых библиотеках вы часто встречаете колбэки. Jest поддерживает и этот вариант. Здесь я размещаю пример, чтобы вам было проще понять:

// callbackFn.js

// Функция, которая принимает колбэк и вызывает его через 100 мс
function doSomethingAsync(callback) {
  setTimeout(() => {
    // Передаем в колбэк результат операции
    callback('result');
  }, 100);
}

module.exports = doSomethingAsync;

Тест с использованием аргумента done:

// callbackFn.test.js
const doSomethingAsync = require('./callbackFn');

test('колбэк вызывается с корректным результатом', (done) => {
  // Вызываем функцию и передаем наш колбэк
  doSomethingAsync((data) => {
    try {
      // Пытаемся выполнить проверку
      expect(data).toBe('result');

      // Если все хорошо, сигнализируем Jest о завершении теста
      done();
    } catch (error) {
      // Если произошла ошибка, передаем ее в done, чтобы тест упал
      done(error);
    }
  });
});

Использование done нужно, чтобы Jest ждал колбэк и не завершал тест сразу.

Жизненный цикл тестов: beforeEach, afterEach и другие

Jest предоставляет хуки, которые позволяют готовить состояние перед тестами и очищать его после.

Основные хуки

  • beforeAll — выполняется один раз перед всеми тестами в блоке describe
  • afterAll — выполняется один раз после всех тестов в блоке describe
  • beforeEach — выполняется перед каждым тестом
  • afterEach — выполняется после каждого теста

Давайте разберемся на примере:

// lifecycle.test.js

let db = [];

// Функция для очистки "базы данных"
function clearDB() {
  db = [];
}

// Функция для инициализации фиктивных данных
function seedDB() {
  db.push({ id: 1, name: 'Иван' });
  db.push({ id: 2, name: 'Мария' });
}

describe('работа с псевдо-базой данных', () => {
  // Перед каждым тестом очищаем и наполняем базу начальными данными
  beforeEach(() => {
    clearDB();
    seedDB();
  });

  // После каждого теста просто очищаем массив
  afterEach(() => {
    clearDB();
  });

  test('количество элементов соответствует начальному состоянию', () => {
    expect(db.length).toBe(2);
  });

  test('мы можем добавлять новые элементы', () => {
    db.push({ id: 3, name: 'Петр' });

    expect(db.length).toBe(3);
    expect(db[2].name).toBe('Петр');
  });
});

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

Мокирование (mock) в Jest

Мокирование — одна из сильных сторон Jest. Оно позволяет подменять зависимости, чтобы изолировать тестируемый модуль.

jest.fn — создание мок-функций

jest.fn создает функцию-заглушку, поведение которой вы можете настраивать.

// mockFn.test.js

test('использование jest.fn', () => {
  // Создаем мок-функцию
  const callback = jest.fn();

  // Передаем мок-функцию, как будто это реальный колбэк
  callback('hello');

  // Проверяем, что функция была вызвана
  expect(callback).toHaveBeenCalled();

  // Проверяем, что она была вызвана один раз
  expect(callback).toHaveBeenCalledTimes(1);

  // Проверяем, с какими аргументами была вызвана функция
  expect(callback).toHaveBeenCalledWith('hello');
});

Вы можете задать возвращаемое значение:

test('мок-функция с заданным возвращаемым значением', () => {
  // Создаем мок, который всегда возвращает 42
  const getAnswer = jest.fn().mockReturnValue(42);

  const result = getAnswer();

  expect(result).toBe(42);
  expect(getAnswer).toHaveBeenCalledTimes(1);
});

Или задать поведение для последовательности вызовов:

test('разное поведение на разные вызовы', () => {
  const mock = jest
    .fn()
    // Первый вызов вернет 1
    .mockReturnValueOnce(1)
    // Второй вызов вернет 2
    .mockReturnValueOnce(2)
    // Все дальнейшие вызовы будут возвращать 0
    .mockReturnValue(0);

  expect(mock()).toBe(1);
  expect(mock()).toBe(2);
  expect(mock()).toBe(0);
  expect(mock()).toBe(0);
});

jest.spyOn — шпионим и частично мокаем

Если вам нужно не просто создать мок-функцию, а подменить реальный метод объекта, удобно использовать jest.spyOn.

// spyOn.test.js

const logger = {
  // Метод логирования, который мы будем "подглядывать"
  log: (message) => {
    console.log(message);
  }
};

function doWork() {
  // В рабочем коде вызывается логгер
  logger.log('Работа выполнена');
}

test('doWork вызывает logger.log', () => {
  // Создаем шпиона на методе logger.log
  const spy = jest.spyOn(logger, 'log').mockImplementation(() => {
    // Здесь мы подменяем оригинальную реализацию на пустую
    // чтобы не печатать ничего в консоль во время теста
  });

  // Вызываем тестируемую функцию
  doWork();

  // Проверяем, что логгер был вызван
  expect(spy).toHaveBeenCalledWith('Работа выполнена');

  // Восстанавливаем оригинальную реализацию после теста
  spy.mockRestore();
});

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

jest.mock — мокирование модулей

Теперь давайте посмотрим, как можно замокать целый модуль. Допустим, у вас есть модуль, который делает HTTP-запрос, а вы хотите тестировать только логику, не трогая сеть.

// api.js

// Псевдо-функция, которая делает запрос куда-то во внешний сервис
async function fetchData() {
  // В реальном коде здесь был бы вызов fetch или axios
  return { status: 'ok', data: [1, 2, 3] };
}

module.exports = { fetchData };

И модуль, который использует этот API:

// service.js
const { fetchData } = require('./api');

// Функция, которую мы хотим протестировать
async function getProcessedData() {
  const response = await fetchData();

  // Если ответ не ок, бросаем ошибку
  if (response.status !== 'ok') {
    throw new Error('Ошибка при получении данных');
  }

  // Возвращаем только массив данных
  return response.data;
}

module.exports = { getProcessedData };

Теперь вы увидите, как это тестируется с мокированием модуля api:

// service.test.js

// Сначала говорим Jest, что модуль ./api нужно замокать
jest.mock('./api');

const { getProcessedData } = require('./service');
// После jest.mock мы можем импортировать замоканную версию
const { fetchData } = require('./api');

test('getProcessedData использует fetchData и возвращает массив', async () => {
  // Настраиваем, что замоканный fetchData вернет нужный объект
  fetchData.mockResolvedValue({ status: 'ok', data: [10, 20, 30] });

  const result = await getProcessedData();

  expect(fetchData).toHaveBeenCalledTimes(1);
  expect(result).toEqual([10, 20, 30]);
});

test('getProcessedData выбрасывает ошибку при status не ok', async () => {
  fetchData.mockResolvedValue({ status: 'error', data: [] });

  // Здесь мы проверяем, что функция отклоняется с ошибкой
  await expect(getProcessedData()).rejects.toThrow(
    'Ошибка при получении данных'
  );
});

Обратите внимание на порядок: сначала вызываем jest.mock('./api'), потом импортируем service и только затем используем мокированный fetchData. Это важно, потому что service подхватывает уже замоканную версию.

Тестирование React-компонентов с Jest и Testing Library

Очень часто Jest используют вместе с React Testing Library. Смотрите, как это выглядит.

Базовая настройка

В проекте на React (Create React App или Vite) Jest often уже настроен. Но если вы добавляете его сами, вам понадобится:

npm install --save-dev @testing-library/react @testing-library/jest-dom

Подключите @testing-library/jest-dom в одном из файлов, которые запускаются перед тестами (например, setupTests.js):

// setupTests.js

// Добавляем дополнительные матчеры, такие как toBeInTheDocument
import '@testing-library/jest-dom';

И укажите setupFilesAfterEnv в jest.config:

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/setupTests.js']
};

Простой тест React-компонента

Давайте посмотрим, что происходит в этом примере:

// Counter.jsx

import React, { useState } from 'react';

export function Counter() {
  // Локальное состояние счетчика
  const [count, setCount] = useState(0);

  // Обработчик клика по кнопке
  const handleClick = () => {
    setCount((prev) => prev + 1);
  };

  return (
    <div>
      {/* Текущее значение счетчика */}
      <span>Текущее значение {count}</span>
      {/* Кнопка, нажатие на которую увеличивает счетчик */}
      <button onClick={handleClick}>Увеличить</button>
    </div>
  );
}

А вот тест:

// Counter.test.jsx

import React from 'react';
// Импортируем функции для рендеринга и взаимодействия с компонентом
import { render, screen, fireEvent } from '@testing-library/react';
// Импортируем тестируемый компонент
import { Counter } from './Counter';

test('счетчик отображает начальное значение и увеличивается при клике', () => {
  // Рендерим компонент в виртуальном DOM
  render(<Counter />);

  // Ищем элемент по тексту
  const spanElement = screen.getByText(/Текущее значение/i);

  // Проверяем, что изначально значение равно 0
  expect(spanElement).toHaveTextContent('Текущее значение 0');

  // Находим кнопку по тексту
  const button = screen.getByText('Увеличить');

  // Имитируем клик по кнопке
  fireEvent.click(button);

  // Проверяем, что текст обновился и теперь значение равно 1
  expect(spanElement).toHaveTextContent('Текущее значение 1');
});

React Testing Library ориентирована на тестирование поведения с точки зрения пользователя, а Jest здесь выступает как тестовый раннер и фреймворк ассертов.

Снапшот-тестирование

Снапшоты помогают отслеживать изменения в структуре результата, например в JSX-компонентах или выходе функции.

Снапшоты для компонентов

Пример с React:

// Hello.jsx

import React from 'react';

// Простой компонент, который показывает приветствие
export function Hello({ name }) {
  return <h1>Привет, {name}</h1>;
}

Тест со снапшотом:

// Hello.test.jsx

import React from 'react';
// Импортируем метод для рендеринга в "дерево" без браузера
import renderer from 'react-test-renderer';
import { Hello } from './Hello';

test('компонент Hello соответствует снепшоту', () => {
  // Создаем тестовый рендер компонента
  const tree = renderer.create(<Hello name="Мир" />).toJSON();

  // Сравниваем с существующим снапшотом
  // Если снапшота нет, Jest создаст его автоматически при первом запуске
  expect(tree).toMatchSnapshot();
});

Когда вы запускаете тест первый раз, Jest создает файл со снапшотом, где хранится JSON-описание структуры компонента. При последующих запусках Jest сравнивает текущий результат с сохраненным.

Если вы осознанно поменяли разметку, вы можете обновить снапшоты командой:

npm test -- --updateSnapshot

Или сокращенно:

npm test -- -u

Снапшоты для обычных функций

Снапшот можно использовать и для объектов:

// formatUser.js

// Функция форматирования данных пользователя для UI
function formatUser(rawUser) {
  return {
    id: rawUser.id,
    fullName: `${rawUser.firstName} ${rawUser.lastName}`,
    isAdult: rawUser.age >= 18
  };
}

module.exports = { formatUser };

Тест:

// formatUser.test.js

const { formatUser } = require('./formatUser');

test('formatUser возвращает корректный объект', () => {
  const raw = { id: 1, firstName: 'Иван', lastName: 'Иванов', age: 20 };

  const formatted = formatUser(raw);

  // Сравниваем результат с сохраненным снапшотом
  expect(formatted).toMatchSnapshot();
});

Снапшоты удобны, когда структура объектов сложная и перечислять все поля в тесте вручную неудобно. Но важно использовать их осознанно: слишком много снапшотов делает тесты хрупкими и тяжело поддерживаемыми.

Работа с таймерами: setTimeout и setInterval

При тестировании кода с таймерами (setTimeout, setInterval) удобно использовать фейковые таймеры.

Включение фейковых таймеров

Покажу вам, как это реализовано на практике:

// timers.js

// Функция, которая через задержку вызывает колбэк
function delayedCall(callback) {
  setTimeout(() => {
    callback('done');
  }, 1000);
}

module.exports = { delayedCall };

Тест с использованием jest.useFakeTimers:

// timers.test.js

const { delayedCall } = require('./timers');

test('delayedCall вызывает колбэк через 1 секунду', () => {
  // Включаем фейковые таймеры
  jest.useFakeTimers();

  // Создаем мок-колбэк
  const callback = jest.fn();

  // Вызываем тестируемую функцию
  delayedCall(callback);

  // На этом этапе таймер еще не сработал
  expect(callback).not.toHaveBeenCalled();

  // Перематываем все таймеры вперед
  jest.runAllTimers();

  // Теперь колбэк должен был вызваться
  expect(callback).toHaveBeenCalledWith('done');

  // Возвращаем реальные таймеры после теста (необязательно в простых случаях, но полезно в больших проектах)
  jest.useRealTimers();
});

Здесь Jest перехватывает вызовы setTimeout и управляет ими сам. Вы можете:

  • jest.runAllTimers() — выполнить все таймеры
  • jest.advanceTimersByTime(ms) — перемотать время вперед на указанное количество миллисекунд
  • jest.runOnlyPendingTimers() — выполнить только ожидающие таймеры

Конфигурация Jest

Jest можно конфигурировать через файл jest.config.js, jest.config.cjs, package.json или jest.config.mjs. Давайте разберем ключевые опции.

Пример базовой конфигурации

// jest.config.js

module.exports = {
  // Базовая директория проекта
  rootDir: '.',

  // Окружение, в котором выполняются тесты
  // node - для бэкенда, jsdom - для фронтенда
  testEnvironment: 'node',

  // Паттерн для поиска тестов
  testMatch: ['**/?(*.)+(test).[jt]s?(x)'],

  // Игнорируемые директории
  testPathIgnorePatterns: ['/node_modules/', '/dist/'],

  // Покрытие кода
  collectCoverage: true,
  collectCoverageFrom: ['src/**/*.{js,jsx}', '!src/index.js'],

  // Папка для отчетов по покрытию
  coverageDirectory: 'coverage',

  // Модули, которые нужно замокать или трансформировать, можно настроить здесь
  // transform, moduleNameMapper и другие
};

Каждая опция влияет на поведение раннера. Например, collectCoverage включает сбор покрытия, но немного замедляет выполнение тестов.

Конфигурация для TypeScript

Для TypeScript-проекта с ts-jest типичный конфиг может выглядеть так:

// jest.config.js

module.exports = {
  preset: 'ts-jest', // Используем ts-jest как пресет
  testEnvironment: 'node',
  testMatch: ['**/?(*.)+(test).[tj]s?(x)'],
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
  collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/index.ts'],
  coverageDirectory: 'coverage'
};

ts-jest сам позаботится о том, чтобы транспилировать TypeScript в JS перед запуском тестов.

Запуск, фильтрация и наблюдение за тестами

Запуск отдельных тестов

Иногда вам нужно запустить только один тест или одну группу. У Jest для этого есть test.only и describe.only.

// only.test.js

describe('группа тестов', () => {
  test('этот тест будет пропущен', () => {
    expect(1 + 1).toBe(3); // Этот тест не выполнится
  });

  test.only('этот тест будет выполнен', () => {
    expect(1 + 1).toBe(2);
  });
});

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

Аналогично есть test.skip и describe.skip — они помечают тесты как пропущенные.

Фильтрация по имени

Вы можете запустить только те тесты, в имени которых встречается определенная подстрока:

npm test -- user

Jest попробует найти все тесты, в имени которых есть user. Это касается и имён файлов, и названий describe/test.

Режим watch

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

npm test -- --watch

Дальше вы можете использовать горячие клавиши (например, p для фильтрации по названию, t — по имени теста, a — запустить все тесты).

Практические советы и лучшие практики

Делайте тесты независимыми

Каждый тест должен:

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

Для этого:

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

Тестируйте поведение, а не реализацию

Хорошие тесты проверяют, что код делает, а не то, как он это делает внутри.

Например, вместо:

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

Лучше:

  • проверять результат работы (возвращаемые значения, состояние БД, изменения в DOM)
  • мокать только внешние зависимости (сеть, файловая система, случайные значения)

Не перегружайте снапшот-тестами

Снапшоты полезны, когда:

  • структура результата сложная
  • вы ожидаете редкие, но значимые изменения

Но если:

  • снапшоты большие
  • они часто меняются при небольших правках
  • вы механически жмете "обновить снапшоты", не анализируя изменения

такие тесты теряют смысл. Старайтесь держать снапшоты маленькими и осмысленными.

Поддерживайте тесты в актуальном состоянии

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

Следите, чтобы:

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

Хорошее покрытие тестами на Jest помогает быстрее находить регрессии, уверенно рефакторить код и уменьшать количество критичных ошибок в продакшене. По мере того как вы осваиваете Jest, имеет смысл постепенно вводить более продвинутые техники — сложное мокирование, снапшоты, интеграцию с CI и анализ покрытия.

Частозадаваемые технические вопросы по теме статьи и ответы на них

Как запустить Jest только для файлов в конкретной директории

Вы можете использовать флаг --runTestsByPath или просто указать путь:

npx jest src/services
# или конкретный файл
npx jest src/services/user.service.test.ts

В скриптах package.json это работает так же:

{
  "scripts": {
    "test:services": "jest src/services"
  }
}

Как настроить алиасы путей (import из @src вместо относительных путей) в Jest

Используйте опцию moduleNameMapper:

// jest.config.js

module.exports = {
  moduleNameMapper: {
    '^@src/(.*)$': '<rootDir>/src/$1'
  }
};

Теперь импорт @src/utils/math в тестах будет сопоставляться с src/utils/math.

Как делать снапшоты с учетом форматирования даты и случайных значений

Обычно такие значения делают предсказуемыми:

  1. Мокируйте Date.now или new Date с помощью jest.spyOn и mockImplementation.
  2. Мокируйте Math.random:
jest.spyOn(global.Math, 'random').mockReturnValue(0.5);
  1. В afterEach или afterAll восстанавливайте оригинальную реализацию через mockRestore.

Так снапшоты будут стабильными.

Как запускать тесты Jest в Docker-контейнере

  1. Установите зависимости внутри контейнера.
  2. Добавьте в Dockerfile:
RUN npm ci
CMD ["npm", "test", "--", "--runInBand"]

Опция --runInBand отключает параллелизм, что повышает стабильность в ограниченных по ресурсам контейнерах.

Как интегрировать Jest с ESLint, чтобы запрещать .only в тестах

Установите плагин:

npm install --save-dev eslint-plugin-jest

В конфигурации ESLint:

// .eslintrc.js

module.exports = {
  plugins: ['jest'],
  extends: ['plugin:jest/recommended'],
  rules: {
    'jest/no-focused-tests': 'error' // запрет test.only и describe.only
  }
};

Теперь линтер будет ругаться, если в коде останутся "сфокусированные" тесты.

Стрелочка влевоФункция append в Go GolangVue DevTools - как эффективно отлаживать приложения на VueСтрелочка вправо

Постройте личный план изучения Vue до уровня Middle — бесплатно!

Vue — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Vue

Руководство по валидации форм во Vue.jsИнтеграция Tiptap для создания редакторов на VueРабота с таблицами во Vue через TanStackИнструкция по установке и компонентам Vue sliderУправление пакетами Vue js с помощью npmУправление пакетами и node modules в Vue проектахКак использовать meta для улучшения SEO на VueПолный гайд по компоненту messages во Vuejs5 правил использования Inertia с Vue и LaravelРабота с модулями и пакетами в VueИнструкция по работе с grid на VueGithub для Vue проектов - подробная инструкция по хранению и совместной работеНастройка ESLint для Vue проектов и поддержка качества кодаОбработка ошибок и отладка в Vue.jsИспользование Vue Devtools для отладки и мониторинга приложенийРабота с конфигурационными файлами и скриптами VueСоздание и настройка проектов Vue с помощью Vue CLI3 способа интеграции Chart.js с Vue для создания графиковРабота с Canvas во VueИнструкция по реализации календаря во VueРабота с Ant Design Vue для создания UI на Vue
Vuex - полное руководство по управлению состоянием во Vue приложенияхРеактивные ссылки ref - полный разбор для разработчиковРеактивные переменные - концепция reactive и практические примерыРеактивные объекты reactive-objects - подробное руководство с примерамиМеханизм Provide Inject - как он работает и когда применятьPinia современный менеджер состояния для VueЛокальное состояние local state в веб разработкеГлобальное состояние в приложениях - global state
Обзор и использование утилит Vue для удобной разработкиРабота с обновлениями компонента и жизненным циклом updateРазрешение конфликтов и ошибок с помощью Vue resolveИспользование query-параметров и их обработка в маршрутах VueЗагрузка и управление состоянием загрузки в VueИспользование библиотек Vue для расширения функционалаРабота с JSON данными в приложениях VueКак работать с экземплярами компонента Instance во VueПолучение данных и API-запросы во Vue.jsЭкспорт и импорт данных и компонентов в VueОбработка событий и их передача между компонентами VuejsГайд по defineEmits на Vue 3Понимание core функционала Vue и его применениеПонимание и применение Composition API в Vue 3Понимание и работа с компилятором VueКогда и как использовать $emit и call во VueВзаимодействие с внешними API через Axios в Vue
Веб приложения на Vue архитектура и лучшие практикиИспользование Vite для быстрого старта и сборки проектов на Vue 3Работа с URL и ссылками в приложениях на VueРабота с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueИспользование Quasar Framework для разработки на Vue с готовыми UI-компонентамиОбзор популярных шаблонов и стартовых проектов на VueИнтеграция Vue с PHP для создания динамичных веб-приложенийКак организовать страницы и маршруты в проекте на VueNuxt JS и Vue 3 для SSR приложенийСоздание серверных приложений на Vue с помощью Nuxt jsИспользование Vue Native для разработки мобильных приложенийОрганизация и управление индексной страницей в проектах VueИспользование Docker для контейнеризации приложений на VueИнтеграция Vue.js с Django для создания полноценных веб-приложенийСоздание и работа с дистрибутивом build dist Vue приложенийРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияКак исправить ошибку cannot find module vueНастройка и сборка проектов Vue с использованием современных инструментовИнтеграция Vue с Bitrix для корпоративных решенийРазработка административных панелей на Vue js
Функция append в Go GolangОтображение компонента mounted - практическое руководствоХуки жизненного цикла компонентов - полное руководство для разработчиковУничтожение компонента destroyed - как правильно очищать ресурсы и подпискиИнициализация данных в состоянии created - как и когда подготавливать данные в приложенииОбновление компонента beforeUpdate во VueМонтирование компонента - хук beforeMount в VueРазрушение компонента во Vue - beforeDestroy и beforeUnmountСоздание экземпляра beforeCreate - полный разбор жизненного цикла
5 библиотек для создания tree view во VueИнтеграция Tailwind CSS с Vue для современных интерфейсовИнтеграция Vue с серверной частью и HTTPS настройкамиКак обрабатывать async операции с Promise во VueИнтеграция Node.js и Vue.js для разработки приложенийРуководство по интеграции Vue js в NET проектыПримеры использования JSX во VueГайд по импорту и регистрации компонентов на VueМногоязычные приложения на Vue с i18nИнтеграция FLIR данных с Vue5 примеров использования filter во Vue для упрощения разработки3 примера реализации drag-and-drop во Vue
Слоты компонента - концепция и практическое использованиеРегистрация компонентов component-registration в приложениях с внедрением зависимостейProps компонента в React - полный разбор с примерамиФункциональные компоненты в React - функциональный подход к построению интерфейсовСобытия компонента - events в современных интерфейсахДинамические компоненты - dynamic-componentsСоздание компонента component - практическое руководствоАсинхронные компоненты async-components - практическое руководство
Наблюдатели watchers - от паттерна до практических реализацийУправление переменными и реактивными свойствами во VueИспользование v for и slot в VueПрименение v-bind для динамической привязки атрибутов в VueУправление пользователями и их данными в Vue приложенияхСоздание и использование UI Kit для Vue приложенийТипизация и использование TypeScript в VuejsШаблоны Vue templates - практическое руководство для разработчиковИспользование шаблонов в Vue js для построения интерфейсовИспользование Swiper для создания слайдеров в VueРабота со стилями и стилизацией в VueСтруктура и особенности Single File Components SFC в VueРабота со SCSS в проектах на Vue для стилизацииРабота со скроллингом и прокруткой в Vue приложенияхПрименение script setup синтаксиса в Vue 3 для упрощения компонентовИспользование scoped стилей для изоляции CSS в компонентах Vue3 способа улучшить навигацию Vue с push()Обработка запросов и асинхронных операций в VueРеактивность Vue reactivity - как это работает под капотом и как этим пользоватьсяПонимание и использование provide inject для передачи данных между компонентамиПередача и использование props в Vue 3 для взаимодействия компонентовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsРабота со свойствами компонентов VueУправление параметрами и динамическими данными во VueОпции компонента в Go - паттерн component-optionsРабота с lifecycle-хуком onMounted во VueОсновы работы с объектами в VueПонимание жизненного цикла компонента Vue js на примере mountedИспользование модальных окон modal в Vue приложенияхИспользование методов в компонентах Vue для обработки логикиИспользование метода map в Vue для обработки массивовИспользование хуков жизненного цикла Vue для управления состоянием компонентаРабота с ключами key в списках и компонентах VueОбработка пользовательского ввода в Vue.jsРабота с изображениями и их оптимизация в VueИспользование хуков жизненного цикла в VueОрганизация сеток и гридов для верстки интерфейсов на VueСоздание и управление формами в VueОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitРабота с динамическими компонентами и данными в Vue3 способа манипулирования DOM на VueРуководство по div во VueИспользование директив в Vue и их расширенные возможностиОсновы и применение директив в VueИспользование директив и их особенности на Vue с помощью defineИспользование компонентов datepicker в Vue для выбора датОрганизация циклов и итераций во VueКак работает компиляция Vue CoreВычисляемые свойства computed во Vue.jsСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в VueИспользование классов в Vue для организации кода и компонентовИспользование директивы checked для управления состоянием чекбоксов в VueГайд на checkbox компонент во VueОтображение данных в виде графиков с помощью Vue ChartСоздание и настройка кнопок в VueСоздание и настройка кнопок в Vue приложенияхРабота с lifecycle-хуками beforeCreate и beforeMount во VueОсновы Vue - vue-basics для уверенного стартаИспользование массивов и методов их обработки в VueИспользование массивов и их обработка в Vue
Использование Vuetify для создания современных интерфейсов на VueИспользование transition во VueТестирование компонентов и приложений на VueТелепортация - архитектура и реализация в серверных приложенияхРабота с teleport для управления DOM во VueSuspense в React - управление асинхронными данными и ленивой загрузкойПять шагов по настройке SSR в VuejsИспользование Shadcn UI компонентов с Vue для продвинутых интерфейсовИспользование router-link для навигации в Vue RouterКак использовать require в Vue для динамического импорта модулейРабота с динамическим рендерингом и виртуальным DOM на Vue.jsИспользование ref для управления ссылками и реактивностью в Vue 3Использование Vue Pro и его преимущества для профессиональной разработкиПлагины Vue vue-plugins - полное практическое руководствоРуководство по nextTick для работы с DOMМиксины - mixins в современном программированииJSX в Vue с использованием плагина vue-jsxСоздание и использование компонентов с помощью Vue js и CУправление состоянием и реактивностью через inject и provideДинамическое обновление компонентов и данных на VueГлубокое изучение документации Vue и как эффективно её использоватьКастомные элементы - Custom Elements в современном JavaScriptИспользование Crystal с Vue для разработкиИспользование вычисляемых свойств для динамического отображения данных на Vue jsОптимизация производительности и предупреждения в Vue
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Vue 3 и Pinia

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.9
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажеры
Практика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий