🧪
Веб-сайты

Модульное тестирование: автоматическая проверка кода (PHPUnit и Jest)

08.08.2025
← Все статьи

Почти каждому разработчику знакома ситуация, когда одно маленькое изменение в коде неожиданно ломает совершенно другую часть приложения. Самый надёжный способ застраховаться от таких сюрпризов — это модульное тестирование. Модульный тест представляет собой небольшую программу, которая автоматически проверяет наименьшую логическую единицу кода, как правило отдельную функцию или метод, в изоляции от остальной системы. Вы передаёте функции определённые входные данные и заранее описываете, какой результат она должна вернуть; если фактический результат не совпадает с ожидаемым, тест проваливается и немедленно сигнализирует о проблеме.

Зачем вообще писать модульные тесты

Первое и самое важное преимущество модульных тестов — это раннее обнаружение ошибок. Логические дефекты, которые легко пропустить при ручной проверке, выявляются автоматическими тестами задолго до того, как код попадёт в продакшен, а это многократно снижает стоимость исправления. Второе преимущество — уверенность в коде: когда сотни тестов горят зелёным, вы точно знаете, что ключевые механизмы системы работают как задумано. Третья и зачастую самая ценная польза проявляется при рефакторинге — тесты выступают страховочной сеткой, потому что если вы случайно что-то сломаете во время переписывания, они тут же станут красными и покажут, что именно перестало работать.

Базовые понятия: assert и паттерн AAA

В сердце любого модульного теста лежит утверждение, или assertion. Утверждение — это заявление вида «я ожидаю, что это значение будет именно таким»; например, вы утверждаете, что функция сложения, принимая 2 и 3, вернёт 5. Если утверждение не выполняется, тест падает. Хорошо структурированные тесты обычно следуют паттерну AAA: на этапе Arrange (подготовка) создаются необходимые данные и объекты, на этапе Act (действие) вызывается проверяемая функция, а на этапе Assert (проверка) результат сравнивается с ожидаемым значением. Чёткое разделение этих трёх этапов делает тест гораздо более читаемым и облегчает его понимание спустя месяцы.

Проверка PHP-кода с помощью PHPUnit

В мире PHP самым распространённым инструментом тестирования является PHPUnit. Он устанавливается через Composer, а тесты обычно хранятся в отдельной папке tests. В примере ниже мы рассмотрим простой класс Calculator и тест для его метода add, в котором паттерн AAA виден особенно наглядно.

<?php
use PHPUnit\Framework\TestCase;

class Calculator {
    public function add(int $a, int $b): int {
        return $a + $b;
    }
}

class CalculatorTest extends TestCase {
    public function testAddReturnsSum(): void {
        // Arrange
        $calc = new Calculator();
        // Act
        $result = $calc->add(2, 3);
        // Assert
        $this->assertSame(5, $result);
    }
}

Чтобы запустить этот тест, в терминале вводят команду vendor/bin/phpunit tests/. Если всё в порядке, PHPUnit покажет зелёный результат и количество пройденных тестов; при наличии дефекта в методе он подробно сообщит, в какой строке какое значение ожидалось и что вернулось на самом деле, что сильно ускоряет поиск причины.

Тестирование JavaScript-функций с помощью Jest

Во фронтенд- и Node.js-проектах самым популярным инструментом тестирования считается Jest. Он выделяется простым синтаксисом и высокой скоростью, при этом не требует сложной настройки и работает почти из коробки. В примере ниже мы видим JavaScript-функцию и её тест на Jest; здесь блок describe группирует связанные тесты, а функция test описывает отдельный случай.

// math.js
function add(a, b) {
  return a + b;
}
module.exports = { add };

// math.test.js
const { add } = require('./math');

describe('функция add', () => {
  test('складывает два положительных числа', () => {
    // Arrange & Act
    const result = add(2, 3);
    // Assert
    expect(result).toBe(5);
  });
});

Тесты запускаются командой npx jest. Jest автоматически находит все файлы с расширением .test.js, выполняет их и выводит результаты с цветовой подсветкой. Пара expect и toBe здесь выполняет ровно ту же роль, что и assertion в PHPUnit, поэтому переход между двумя инструментами оказывается интуитивно понятным.

Моки: замена внешних зависимостей

В реальных проектах функции часто обращаются к базе данных, внешнему API или платёжной системе. Запускать такие зависимости по-настоящему в каждом тесте было бы медленно и ненадёжно, поэтому вместо них подставляют мок, то есть подделку. Мок — это контролируемый объект, который выглядит как настоящая зависимость, но возвращает заранее заданные результаты. Например, замокав метод, который достаёт пользователя из базы, вы гарантируете, что он всегда вернёт одного и того же тестового пользователя; тогда тест проверяет только собственную логику и не зависит от состояния внешней системы. В Jest это делается через jest.fn(), а в PHPUnit — через createMock().

Покрытие кода и краткое введение в TDD

Покрытие кода, или code coverage, — это метрика, показывающая, какой процент строк вашего кода был выполнен во время тестов. PHPUnit генерирует такой отчёт с флагом --coverage-html, а Jest — с флагом --coverage, и оба наглядно показывают, какие строки вообще не были протестированы. Важно понимать, что высокий процент покрытия сам по себе не гарантирует качества, но низкое покрытие надёжно выявляет опасные неохваченные зоны. Разработка через тестирование (TDD) — это подход, при котором вы сначала пишете тест, он закономерно падает, затем пишете минимальный код, проходящий этот тест, и только потом причёсываете код. Такой цикл вынуждает с самого начала проектировать код простым и легко тестируемым.

Когда и как именно писать тесты

Покрывать тестами абсолютно всё не обязательно и зачастую бесполезно. В первую очередь стоит тестировать функции со сложной бизнес-логикой, множеством условий и вычислений, потому что именно там чаще всего возникают ошибки. Если вы нашли и исправили какой-то баг, очень полезно написать тест, воспроизводящий именно этот баг, поскольку это гарантирует, что в будущем он не вернётся незаметно. Но самое главное — превратить написание тестов в привычку: если, создавая новую функцию, вы сразу пишете тест к ней, со временем в проекте формируется надёжная страховочная сетка, и каждое последующее изменение даётся гораздо спокойнее.

Похожие статьи

🌾 Сайт сельского хозяйства и агробизнеса: каталог продукции и B2B-продажи ❤️ Сайт благотворительного фонда: прозрачный сбор и доверие донора 🎉 Сайт банкетного зала и места для свадьбы: организация события и онлайн-бронь 🚙 Сайт аренды автомобилей: каталог авто, калькулятор цен и онлайн-бронь
🌐 Язык
🇺🇿 O'zbek 🇺🇿 Ўзбек 🇷🇺 Русский 🇬🇧 English