Тест - это маленькая программа, которая проверяет, что другая программа делает то, что обещает. В Jest у него всегда одна форма: функция test принимает имя и тело - callback, то есть функцию, которую Jest вызовет за вас. Внутри тела пишут expect от фактического значения и присоединяют матчер - метод, задающий ожидание. Сквозной пример книги - сервис заказа, а первая проверяемая функция calculateDelivery берёт заказ и возвращает стоимость доставки. С неё и начнём.
Начинать стоит с самого маленького честного теста, а не с фреймворка целиком. Возьмём одно правило: доставка стоит 300 рублей для обычного заказа. Тест из трёх строк - имя, вызов функции, ожидание - уже документирует правило и защищает его от будущих правок. Имя пишут как утверждение о поведении: 'берёт 300 рублей за обычную доставку', а не 'тест calculateDelivery'.
Разберём форму построчно. test - глобальная функция, её не нужно импортировать; первый аргумент - строка-имя, второй - callback без аргументов. Вызов expect(calculateDelivery(order)) оборачивает фактический результат в объект проверки, а toBe(300) - матчер, сверяющий его с ожидаемым через Object.is. У test есть точный синоним it: запись it('charges ...') читается как английское предложение, и обе формы взаимозаменяемы - выбор чисто стилистический.
Когда тестов у одной функции несколько, их собирают в группу через describe. Первый аргумент - имя элемента системы, обычно функции или компонента; второй - callback, внутри которого лежат test. Группа не меняет поведение проверок, но даёт две вещи: в отчёте тесты печатаются с отступом под общим заголовком, и появляется естественное место для общей подготовки через beforeEach.
Запуск - одна команда. npx jest находит все файлы с суффиксом .test и прогоняет их, печатая зелёную галочку на каждый прошедший тест и красный крест на упавший. Во время разработки удобнее watch-режим: npx jest --watch следит за файлами и перезапускает только затронутые тесты - цикл правки и проверки сжимается до секунд. В CI watch не нужен и даже вреден, там запускают простой jest.
Главный навык новичка - читать падение, а не пугаться его. Когда матчер не сошёлся, Jest печатает три вещи: имя упавшего теста, блок Expected против Received и точную строку с указателем. Expected - то, что вы написали в матчере, Received - то, что реально вернул код. Если там Expected 300, а Received 250, вопрос не в тесте, а в том, почему функция вернула 250. Красный вывод - не наказание, а адрес расхождения.
FAIL src/delivery.test.ts
✕ берёт 300 рублей за обычную доставку
expect(received).toBe(expected) // Object.is equality
Expected: 300
Received: 250
6 | expect(calculateDelivery(order)).toBe(300)
| ^Отсюда вырастает главный ритм - красно-зелёный цикл. Сначала пишут тест на поведение, которого ещё нет, и запускают его - он обязан упасть красным; это доказывает, что тест вообще способен ловить ошибку, а не проходит впустую. Затем пишут минимальный код, чтобы тест позеленел, - ровно столько, сколько нужно, без запаса на будущее. Красный до зелёного не бюрократия: тест, который вы ни разу не видели красным, может молча проходить из-за опечатки в имени матчера.
Цена дисциплины - минуты на лишний прогон и привычка сначала думать об ожидаемом результате. Выгода - что каждый тест доказал способность падать, а набор растёт как проверенная спецификация поведения. Типичный провал обратного подхода: тест пишут сразу зелёным, ни разу не увидев красного, а через месяц выясняется, что expect стоял без матчера или сравнивал значение само с собой - и не поймал ни одной регрессии.
import { calculateDelivery } from './delivery.js'
test('берёт 300 рублей за обычную доставку', () => {
const order = { total: 1_000, premium: false }
expect(calculateDelivery(order)).toBe(300)
})