Codeception для PHP: полное руководство с примерами

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

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

Начать курс

Codeception для PHP: полное руководство с примерами

1. Что это и зачем нужна

Codeception — это полнофункциональный фреймворк для тестирования PHP-приложений, который объединяет в себе лучшие практики модульного, функционального, приемочного (acceptance) и интеграционного тестирования. В отличие от стандартного PHPUnit, Codeception предоставляет единый, интуитивно понятный API для всех типов тестов, а также мощные инструменты для работы с браузерами (через WebDriver или PhpBrowser), базами данных, REST API и даже JavaScript.

Основная цель Codeception — сделать процесс написания тестов максимально простым и читаемым. Фреймворк использует BDD-подобный синтаксис (Behavior-Driven Development), где тесты описываются на естественном языке с помощью методов-действий: $I->amOnPage('/login'), $I->fillField('email', 'user@example.com'), $I->click('Login'). Это позволяет не только разработчикам, но и тестировщикам, аналитикам и менеджерам легко понимать, что именно проверяется.

Codeception незаменим в проектах, где требуется комплексное тестирование: от проверки отдельных классов (unit-тесты) до симуляции действий реального пользователя в браузере (acceptance-тесты). Он поддерживает популярные фреймворки (Laravel, Symfony, Yii2, Zend), автоматически управляет транзакциями базы данных, умеет делать скриншоты при ошибках и генерировать подробные отчёты. Если вы хотите повысить качество кода и сократить время на регрессионное тестирование — Codeception станет вашим надёжным помощником.

2. Установка

Codeception распространяется через Composer — стандартный менеджер пакетов для PHP. Убедитесь, что у вас установлен PHP версии 7.4 или выше и Composer.

Выполните в терминале в корне вашего проекта:

composer require --dev codeception/codeception

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

vendor/bin/codecept init

Эта команда создаст базовую структуру директорий: tests/ с подпапками unit, functional, acceptance, а также файл конфигурации codeception.yml.

Для работы с браузерными тестами дополнительно установите модуль WebDriver (если планируете использовать Selenium или ChromeDriver):

composer require --dev codeception/module-webdriver

Для тестирования REST API установите модуль REST:

composer require --dev codeception/module-rest

3. Быстрый старт — минимальный рабочий пример

Создадим простой unit-тест для проверки математической функции. После инициализации (codecept init) у вас уже есть файл tests/unit.suite.yml. Отредактируем его, чтобы подключить модуль Asserts:

# tests/unit.suite.yml
actor: UnitTester
modules:
    enabled:
        - Asserts

Теперь создадим тест-файл tests/unit/MathTest.php:

<?php
use Codeception\Test\Unit;

class MathTest extends Unit
{
    protected UnitTester $tester;

    public function testAddition()
    {
        $result = 2 + 3;
        $this->tester->assertEquals(5, $result, '2 + 3 должно быть равно 5');
    }

    public function testSubtraction()
    {
        $result = 10 - 4;
        $this->tester->assertGreaterThan(5, $result, '10 - 4 должно быть больше 5');
    }
}

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

vendor/bin/codecept run unit

Вы должны увидеть зелёный отчёт с двумя успешными тестами. Поздравляю, вы написали первый тест на Codeception!

4. Основные методы/функции/классы

Ниже приведены ключевые методы, которые вы будете использовать чаще всего. Они доступны через объект $I (Actor) в каждом типе тестов.

4.1. amOnPage()

Сигнатура: void amOnPage(string $page)

Что делает: Переходит на указанный URL относительно базового URL, заданного в конфигурации. Используется в acceptance- и functional-тестах.

<?php
// В acceptance-тесте
$I->amOnPage('/login');
$I->see('Войти'); // Проверяем, что на странице есть текст "Войти"

4.2. fillField()

Сигнатура: void fillField(string|array $field, string $value)

Что делает: Заполняет текстовое поле (input, textarea) указанным значением. Поле можно найти по имени, ID, CSS-классу или XPath.

<?php
$I->amOnPage('/register');
$I->fillField('input[name="email"]', 'test@example.com');
$I->fillField('#password', 'securePass123');

4.3. click()

Сигнатура: void click(string|array $link, array $context = [])

Что делает: Имитирует клик по кнопке, ссылке или любому другому элементу. Можно указать текст, CSS-селектор или XPath.

<?php
$I->click('Submit'); // Клик по кнопке с текстом "Submit"
$I->click('#login-button'); // Клик по элементу с ID login-button
$I->click('a[href="/logout"]'); // Клик по ссылке

4.4. see()

Сигнатура: void see(string $text, ?string $selector = null)

Что делает: Проверяет, что на текущей странице присутствует указанный текст. Если передан селектор, проверка выполняется только внутри этого элемента.

<?php
$I->amOnPage('/dashboard');
$I->see('Добро пожаловать'); // Текст есть на странице
$I->see('Ошибка', '.alert-danger'); // Текст внутри элемента с классом alert-danger

4.5. dontSee()

Сигнатура: void dontSee(string $text, ?string $selector = null)

Что делает: Обратный метод к see() — проверяет, что текст отсутствует на странице.

<?php
$I->amOnPage('/login');
$I->dontSee('Ошибка авторизации'); // Убеждаемся, что сообщения об ошибке нет

4.6. grabFromCurrentUrl()

Сигнатура: string grabFromCurrentUrl(?string $pattern = null)

Что делает: Возвращает текущий URL или его часть, соответствующую регулярному выражению. Полезно для проверки редиректов.

<?php
$I->amOnPage('/old-page');
$I->seeCurrentUrlEquals('/new-page'); // Проверка полного URL
$id = $I->grabFromCurrentUrl('/\/user\/(\d+)/'); // Извлечь ID пользователя из URL

4.7. haveInDatabase()

Сигнатура: int haveInDatabase(string $table, array $data)

Что делает: Вставляет запись в базу данных (требуется модуль Db). Возвращает ID вставленной записи. Используется для подготовки тестовых данных.

<?php
// В functional-тесте
$userId = $I->haveInDatabase('users', [
    'email' => 'test@example.com',
    'name' => 'John Doe'
]);
$I->amOnPage('/user/' . $userId);
$I->see('John Doe');

4.8. sendAjaxPostRequest()

Сигнатура: void sendAjaxPostRequest(string $uri, array $params = [])

Что делает: Отправляет AJAX POST-запрос на указанный URI. Полезно для тестирования API-эндпоинтов, работающих через JavaScript.

<?php
$I->sendAjaxPostRequest('/api/task', ['title' => 'New Task']);
$I->seeResponseCodeIs(201); // Проверка HTTP-статуса
$I->seeResponseContains('"id":'); // Проверка JSON-ответа

4.9. seeInDatabase()

Сигнатура: void seeInDatabase(string $table, array $criteria)

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

<?php
$I->amOnPage('/register');
$I->fillField('email', 'new@user.com');
$I->click('Register');
$I->seeInDatabase('users', ['email' => 'new@user.com']); // Подтверждаем, что пользователь создан

4.10. executeInSelenium()

Сигнатура: mixed executeInSelenium(\Closure $function)

Что делает: Выполняет произвольный код через WebDriver (Selenium). Позволяет получить доступ к низкоуровневым функциям браузера, например, выполнить JavaScript.

<?php
$I->executeInSelenium(function (\Facebook\WebDriver\Remote\RemoteWebDriver $webdriver) {
    $title = $webdriver->getTitle();
    $webdriver->manage()->window()->maximize();
    return $title;
});

5. Реальный практический пример (100-200 строк кода)

Рассмотрим тестирование процесса регистрации пользователя на сайте. Мы создадим acceptance-тест, который симулирует действия реального пользователя: открывает страницу регистрации, заполняет форму, отправляет её и проверяет результат.

Предположим, у нас есть Laravel-приложение. Сначала настроим tests/acceptance.suite.yml:

# tests/acceptance.suite.yml
actor: AcceptanceTester
modules:
    enabled:
        - WebDriver:
            url: 'http://localhost:8000'
            browser: chrome
            window_size: 1280x1024
        - Db:
            dsn: 'mysql:host=localhost;dbname=testdb'
            user: 'root'
            password: ''
            populate: true
            cleanup: true

Теперь сам тест tests/acceptance/RegistrationCest.php:

<?php
use Codeception\Util\Fixtures;

class RegistrationCest
{
    private string $testEmail = 'user_' . uniqid() . '@example.com';
    private string $testPassword = 'SecurePass123!';

    /**
     * @before
     */
    public function _before(AcceptanceTester $I)
    {
        // Убедимся, что пользователь не залогинен
        $I->amOnPage('/logout');
    }

    public function testSuccessfulRegistration(AcceptanceTester $I)
    {
        // 1. Открываем страницу регистрации
        $I->amOnPage('/register');
        $I->see('Регистрация', 'h1');

        // 2. Заполняем форму
        $I->fillField('input[name="name"]', 'Иван Петров');
        $I->fillField('input[name="email"]', $this->testEmail);
        $I->fillField('input[name="password"]', $this->testPassword);
        $I->fillField('input[name="password_confirmation"]', $this->testPassword);

        // 3. Отправляем форму
        $I->click('button[type="submit"]');

        // 4. Проверяем, что пользователь перенаправлен на дашборд
        $I->seeCurrentUrlEquals('/dashboard');
        $I->see('Добро пожаловать, Иван Петров!');

        // 5. Проверяем, что в БД появилась запись
        $I->seeInDatabase('users', [
            'email' => $this->testEmail,
            'name' => 'Иван Петров'
        ]);

        // 6. Проверяем, что письмо с подтверждением отправлено (если есть)
        $I->seeInDatabase('email_log', [
            'recipient' => $this->testEmail,
            'subject' => 'Подтверждение регистрации'
        ]);
    }

    public function testRegistrationWithExistingEmail(AcceptanceTester $I)
    {
        // Сначала создадим пользователя через БД
        $existingEmail = 'existing@example.com';
        $I->haveInDatabase('users', [
            'email' => $existingEmail,
            'name' => 'Existing User',
            'password' => password_hash('test123', PASSWORD_DEFAULT)
        ]);

        // Пытаемся зарегистрироваться с тем же email
        $I->amOnPage('/register');
        $I->fillField('name', 'New User');
        $I->fillField('email', $existingEmail);
        $I->fillField('password', 'NewPass123!');
        $I->fillField('password_confirmation', 'NewPass123!');                    

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