BDD на PHP: Пишем код, который понимает бизнес

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему код не работает, если бизнес сказал "всё верно"?

Вы когда-нибудь сталкивались с ситуацией: менеджер утверждает, что задача решена, тесты проходят зелёным, а пользователь всё равно недоволен? Классика жанра. Проблема не в коде — проблема в коммуникации. Мы говорим на разных языках: бизнес оперирует сценариями, а разработчики — методами и классами. Здесь на помощь приходит BDD (Behaviour-Driven Development) — разработка через поведение. И да, на PHP это работает блестяще.

В этой статье мы разберёмся, как BDD на PHP помогает превратить расплывчатые "хотелки" в понятные тесты, которые понимают и менеджер, и программист. Вы узнаете, чем BDD отличается от обычного TDD, как писать сценарии на Gherkin и какие инструменты (Behat, PHPSpec) реально упрощают жизнь.

Что такое BDD простыми словами?

BDD (Behaviour-Driven Development) — это эволюция TDD. Если TDD фокусируется на "как тестировать код", то BDD отвечает на вопрос "что должен делать код, чтобы бизнес был счастлив".

Представьте, что вы пишете интернет-магазин. Вместо абстрактного теста "метод applyDiscount() должен уменьшить цену", вы пишете сценарий:

  • Дано: в корзине товар за 1000 рублей
  • Когда: пользователь вводит промокод "SALE10"
  • Тогда: стоимость корзины становится 900 рублей

Этот сценарий читает любой человек — от маркетолога до тимлида. Никакой магии, только поведение системы.

BDD vs TDD: в чём разница?

Многие путают эти понятия. Давайте расставим точки над i:

  • TDD — пишем тест, потом код, потом рефакторим. Тесты пишет разработчик для разработчика.
  • BDD — сначала пишем сценарий поведения на естественном языке, потом автоматизируем его. Сценарии пишутся в диалоге с бизнесом.

BDD не отменяет TDD, а дополняет его. Вы всё ещё пишете юнит-тесты, но теперь у вас есть "живая документация" в виде сценариев.

Инструменты BDD для PHP: выбираем оружие

Экосистема PHP предлагает два основных инструмента для разработки через поведение: Behat и PHPSpec. Они решают разные задачи.

Behat — универсальный солдат

Behat — это фреймворк для приёмочного тестирования. Он работает с языком Gherkin (тот самый "Дано-Когда-Тогда"). Behat идеален для интеграционных тестов, проверки API, веб-интерфейсов и бизнес-логики верхнего уровня.

PHPSpec — микроскоп для разработчика

PHPSpec — это инструмент для спецификации классов. Он ближе к TDD, но с философией BDD: вы описываете поведение объекта, а не его внутренности. PHPSpec отлично подходит для юнит-тестирования доменной логики.

В этой статье мы сосредоточимся на Behat, так как он лучше всего иллюстрирует behaviour-driven development в PHP.

Пишем первый BDD-сценарий на PHP с Behat

Давайте создадим реальный пример. Предположим, мы разрабатываем систему лояльности. Клиент получает скидку 10%, если сумма заказа превышает 5000 рублей.

Шаг 1: Установка Behat

composer require --dev behat/behat

Затем инициализируем проект:

vendor/bin/behat --init

Структура будет такой:

features/
  bootstrap/
    FeatureContext.php
  discount.feature

Шаг 2: Пишем feature-файл на Gherkin

Создаём файл features/discount.feature:

Feature: Расчёт скидки для лояльных клиентов
  As a менеджер по продажам
  I want to предоставлять скидку 10% на заказы от 5000 рублей
  So that стимулировать крупные покупки

  Scenario: Заказ превышает порог скидки
    Given в корзине товары на сумму 6000 рублей
    When я запрашиваю итоговую стоимость
    Then итоговая стоимость должна быть 5400 рублей

  Scenario: Заказ меньше порога скидки
    Given в корзине товары на сумму 3000 рублей
    When я запрашиваю итоговую стоимость
    Then итоговая стоимость должна быть 3000 рублей

Обратите внимание: здесь нет ни строчки PHP. Это и есть суть BDD — бизнес-сценарий понятен без кода.

Шаг 3: Реализуем контекст (FeatureContext)

Теперь нужно "оживить" эти сценарии. Открываем features/bootstrap/FeatureContext.php:

<?php

use Behat\Behat\Context\Context;
use Behat\Gherkin\Node\PyStringNode;
use Behat\Gherkin\Node\TableNode;

class FeatureContext implements Context
{
    private float $cartTotal = 0;
    private float $finalTotal = 0;

    /**
     * @Given в корзине товары на сумму :amount рублей
     */
    public function вКорзинеТоварыНаСуммуРублей(float $amount): void
    {
        $this->cartTotal = $amount;
    }

    /**
     * @When я запрашиваю итоговую стоимость
     */
    public function яЗапрашиваюИтоговуюСтоимость(): void
    {
        $discountService = new DiscountService();
        $this->finalTotal = $discountService->calculate($this->cartTotal);
    }

    /**
     * @Then итоговая стоимость должна быть :expected рублей
     */
    public function итоговаяСтоимостьДолжнаБытьРублей(float $expected): void
    {
        if ($this->finalTotal !== $expected) {
            throw new \Exception(
                "Ожидалось {$expected}, получено {$this->finalTotal}"
            );
        }
    }
}

Видите, как аннотации (например, @Given) связывают текст сценария с PHP-кодом? Это и есть разработка через поведение в действии.

Шаг 4: Пишем бизнес-логику

Создаём класс DiscountService.php:

<?php

class DiscountService
{
    private const THRESHOLD = 5000;
    private const DISCOUNT_RATE = 0.1;

    public function calculate(float $total): float
    {
        if ($total >= self::THRESHOLD) {
            return $total * (1 - self::DISCOUNT_RATE);
        }
        return $total;
    }
}

Шаг 5: Запускаем тесты

vendor/bin/behat

Вы увидите зелёные строки — все сценарии пройдены. Теперь, если бизнес решит изменить порог скидки, он просто редактирует feature-файл, и вы сразу видите, какие тесты сломались.

Лучшие практики B

Блоги

Рекомендаций по книгам