BDD на PHP: Как писать код, который понимает даже менеджер

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

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

Начать курс

Когда код говорит на языке бизнеса: знакомство с BDD

Представьте себе ситуацию: вы написали идеальный, с точки зрения кода, модуль оплаты. Все методы работают, исключения обрабатываются, покрытие юнит-тестами — 99%. Вы открываете шампанское, но приходит менеджер и говорит: «А почему у нас списываются деньги до подтверждения заказа?». И вы понимаете — баг не в коде, а в понимании задачи.

Знакомо? Именно для таких случаев существует BDD (Behaviour-Driven Development) — разработка, управляемая поведением. Это не просто очередная методология, а мост между «хочу, чтобы работало» и «как это реализовать на PHP». Если вы устали переписывать логику после каждого уточнения требований, эта статья для вас.

В этом материале мы разберём, как внедрить BDD на PHP с помощью фреймворка Behat, напишем реальные тесты и поймём, почему это делает код чище, а коммуникацию — прозрачнее.

Что такое BDD и чем он отличается от TDD?

Многие путают Behaviour-Driven Development с обычным TDD (Test-Driven Development). И правда, внешне они похожи: и там, и там сначала пишутся тесты. Но разница — в фокусе.

  • TDD — это про «как проверить, что функция возвращает правильное число». Тесты пишутся на языке программиста: assert, equals, mock.
  • BDD — это про «как система ведёт себя в конкретной бизнес-ситуации». Сценарии пишутся на естественном, почти человеческом языке.

Ключевая идея BDD в PHP — использовать единый язык для общения. Разработчики, тестировщики и менеджеры читают один и тот же сценарий. И если в нём написано «пользователь добавляет товар в корзину без авторизации», то все понимают это одинаково.

Инструментарий: Behat и Gherkin

В мире PHP главным инструментом для BDD является Behat. Это фреймворк, который парсит сценарии на языке Gherkin и превращает их в исполняемые тесты. Gherkin — это простой английский (или русский) язык с ключевыми словами: Feature, Scenario, Given, When, Then.

Давайте установим Behat в ваш PHP-проект:

composer require --dev behat/behat behat/mink-extension behat/mink-goutte-driver

После установки инициализируем проект:

vendor/bin/behat --init

Структура будет выглядеть так:

features/
  bootstrap/
    FeatureContext.php
  your_feature.feature
behat.yml

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

Предположим, у нас есть интернет-магазин. Бизнес-требование: «Зарегистрированный пользователь может добавить товар в корзину и увидеть общую сумму». Создадим файл features/cart.feature:

Feature: Корзина покупателя
  Как зарегистрированный пользователь
  Я хочу добавлять товары в корзину
  Чтобы видеть итоговую стоимость

  Scenario: Добавление одного товара
    Given я захожу на сайт как пользователь "ivan@example.com"
    And на странице каталога есть товар "Ноутбук" за 50000 рублей
    When я нажимаю "Добавить в корзину" для товара "Ноутбук"
    Then я вижу сообщение "Товар добавлен"
    And итоговая сумма корзины равна 50000 рублей

Обратите внимание: этот сценарий может прочитать любой менеджер без знания PHP. Но для Behat он — исполняемый тест.

Реализация шагов (Step Definitions)

Теперь нужно «научить» Behat понимать эти шаги. Откройте 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 $currentUser;
    private $cart = [];
    private $products = [];

    /**
     * @Given я захожу на сайт как пользователь :email
     */
    public function iLoginAsUser($email)
    {
        $this->currentUser = $email;
        // Здесь могла бы быть логика аутентификации
    }

    /**
     * @Given на странице каталога есть товар :name за :price рублей
     */
    public function thereIsProduct($name, $price)
    {
        $this->products[$name] = (int) $price;
    }

    /**
     * @When я нажимаю :button для товара :productName
     */
    public function iAddToCart($button, $productName)
    {
        if ($button === 'Добавить в корзину') {
            $this->cart[] = $this->products[$productName];
        }
    }

    /**
     * @Then я вижу сообщение :message
     */
    public function iSeeMessage($message)
    {
        if ($message !== 'Товар добавлен') {
            throw new \Exception("Ожидалось сообщение: $message");
        }
    }

    /**
     * @Then итоговая сумма корзины равна :sum рублей
     */
    public function cartTotalEquals($sum)
    {
        $total = array_sum($this->cart);
        if ($total !== (int) $sum) {
            throw new \Exception(
                "Сумма корзины $total, ожидалось $sum"
            );
        }
    }
}

Запускаем тест:

vendor/bin/behat features/cart.feature

Если всё зелёное — вы только что написали свой первый BDD-тест на PHP. Если красное — Behat покажет, какой именно шаг упал и почему.

Продвинутые сценарии: таблицы и контексты

В реальных проектах Behaviour-Driven Development становится мощнее, когда вы используете таблицы данных (Data Tables) и Outline-сценарии. Это позволяет проверить сразу несколько кейсов без дублирования кода.

Scenario Outline: проверка граничных значений

Scenario Outline: Проверка скидки на сумму корзины
  Given я зарегистрированный пользователь
  And в корзине товары на сумму <сумма> рублей
  When я применяю промокод "SKIDKA10"
  Then итоговая сумма равна <ожидаемая> рублей

  Examples:
    | сумма | ожидаемая |
    | 1000  | 900       |
    | 5000  | 4500      |
    | 100   | 100       |

Обратите внимание: мы не пишем три разных сценария. Behat сам подставит значения из таблицы и выполнит тест трижды. Это экономит часы работы и делает BDD на PHP максимально эффективным.

Интеграция с Mink: тестирование UI

До сих пор мы тестировали только логику. Но BDD часто применяют для проверки пользовательского интерфейса. Для этого в связке с Behat используют Mink — эмулятор браузера.

Подключите Mink в behat.yml:

default:
  extensions:
    Behat\MinkExtension:
      base_url                

Блоги

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