Разработка без тестов — это как езда на автомобиле без ремня безопасности: вроде едешь, но в любой момент может прилететь. Особенно когда проект растёт, а сроки поджимают. Я сам долго откладывал тесты, думая, что это слишком сложно и скучно. Но когда попробовал Codeception в связке с Yii2, понял: это не больно, а даже удобно. В этой статье расскажу, как подружить эти два инструмента и сделать свою жизнь проще.

Digital-студия WNDER

Ссылка на оригинальную статью

Почему именно Codeception?

На рынке есть много инструментов для тестирования: PHPUnit, Behat, PHPSpec. Но Codeception выделяется тем, что он как швейцарский нож: умеет всё и сразу. Он поддерживает три уровня тестов — юнит, функциональные и приёмочные. А ещё он интегрируется с Yii2 буквально из коробки.

Представьте, что вы строите дом. Юнит-тесты — это проверка каждого кирпича отдельно. Функциональные — проверка, что стены стоят ровно. А приёмочные — что в доме можно жить. Codeception позволяет проверить все три уровня, не переключаясь между разными инструментами.

Плюс у Codeception очень понятный синтаксис. Даже если вы никогда не писали тесты, разберётесь за вечер. Например, чтобы проверить, что страница открывается, нужно всего несколько строк.

Настройка Codeception в Yii2

Сначала установим Codeception через Composer. Если вы ещё не знакомы с Composer — это менеджер пакетов для PHP, как pip для Python. В корне проекта выполните:

composer require --dev codeception/codeception yii2-codeception

После установки нужно инициализировать Codeception. Для этого в папке tests выполните команду:

vendor/bin/codecept init

Эта команда создаст базовую структуру: папки unit, functional, acceptance, а также файлы конфигурации. В Yii2 обычно уже есть папка tests с базовыми настройками, так что можно просто подправить конфиг.

Теперь настроим подключение к базе данных. В файле tests/codeception.yml укажите параметры подключения, чтобы тесты использовали отдельную базу (например, с суффиксом _test). Это важно, чтобы не засорять рабочую базу.

modules:
    config:
        Db:
            dsn: 'mysql:host=localhost;dbname=myapp_test'
            user: 'root'
            password: ''
            dump: 'tests/_data/dump.sql'
            populate: true
            cleanup: true

После этого можно запустить тесты командой vendor/bin/codecept run. Если всё настроено правильно, вы увидите зелёные строки — значит, тесты работают.

Пишем юнит-тесты для моделей

Юнит-тесты — это самый простой уровень. Они проверяют отдельные методы классов. Например, у вас есть модель User с методом getFullName(). Напишем тест, который проверяет, что метод правильно склеивает имя и фамилию.

Создайте файл tests/unit/models/UserTest.php:

<?php
namespace tests\unit\models;

use app\models\User;
use Codeception\Test\Unit;

class UserTest extends Unit
{
    public function testGetFullName()
    {
        $user = new User();
        $user->first_name = 'Иван';
        $user->last_name = 'Петров';

        $this->assertEquals('Иван Петров', $user->getFullName());
    }
}

Здесь мы создаём объект модели, задаём атрибуты и проверяем, что метод возвращает ожидаемую строку. Если метод работает неправильно, тест упадёт — и вы сразу узнаете, что что-то сломано.

Юнит-тесты особенно полезны для проверки бизнес-логики: расчётов, валидации, обработки данных. Они выполняются быстро, поэтому их можно гонять хоть после каждого изменения.

Функциональные тесты: проверяем работу сайта изнутри

Функциональные тесты имитируют запросы к приложению, но без реального браузера. Они проверяют, что контроллеры, экшены и представления работают правильно. Это как если бы вы отправляли запросы через curl, но с дополнительными проверками.

Например, проверим, что страница входа открывается и показывает форму. Создайте tests/functional/LoginCest.php:

<?php
namespace tests\functional;

use FunctionalTester;

class LoginCest
{
    public function testLoginPageWorks(FunctionalTester $I)
    {
        $I->amOnPage('/site/login');
        $I->see('Login', 'h1');
        $I->fillField('#login-form-username', 'admin');
        $I->fillField('#login-form-password', 'password');
        $I->click('Login');
        $I->see('Logout');
    }
}

Что здесь происходит? Мы открываем страницу /site/login, проверяем, что заголовок содержит слово Login, заполняем поля формы и отправляем её. После отправки ожидаем, что появится кнопка Logout — значит, мы успешно вошли.

Функциональные тесты работают быстрее приёмочных, потому что не используют браузер. Но они всё равно проверяют весь стек: маршрутизацию, контроллеры, модели, представления.

Приёмочные тесты: эмулируем действия пользователя

Приёмочные тесты — это самый близкий к реальности уровень. Они запускают настоящий браузер (через WebDriver) и имитируют клики, ввод текста, переходы по страницам. Это как если бы вы наняли тестировщика, который сидит и щёлкает по сайту.

Для работы приёмочных тестов нужен Selenium Server или ChromeDriver. Но не пугайтесь, настройка несложная. Главное — указать в конфиге адрес сервера.

modules:
    config:
        WebDriver:
            url: 'http://localhost:8080'
            browser: chrome
            window_size: 1280x1024

Пример приёмочного теста для регистрации:

<?php
namespace tests\acceptance;

use AcceptanceTester;

class RegistrationCest
{
    public function testRegistration(AcceptanceTester $I)
    {
        $I->amOnPage('/site/signup');
        $I->fillField('Имя', 'Иван');
        $I->fillField('Email', 'ivan@example.com');
        $I->fillField('Пароль', 'secret123');
        $I->click('Зарегистрироваться');
        $I->see('Добро пожаловать');
    }
}

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

Практические советы и лайфхаки

Теперь поделюсь тем, что сам набил шишками. Эти советы сэкономят вам кучу времени.

Правило: Всегда используйте отдельную базу данных для тестов. Никогда не запускайте тесты на рабочей базе — это как играть с огнём. Настройте автоматическое создание и очистку тестовой БД через миграции.

Вот ещё несколько лайфхаков:

  • Группируйте тесты по фичам. Например, все тесты для модуля заказов поместите в папку order. Так проще ориентироваться и запускать только нужное.
  • Используйте фабрики данных. Вместо ручного создания объектов в каждом тесте, используйте Fixtures или Faker. Это ускоряет написание тестов и делает их чище.
  • Пишите тесты сразу после написания кода. Если отложить на потом, забудете, что проверять. Лучше писать тесты параллельно с разработкой.
  • Не гонитесь за 100% покрытием. Это миф, что нужно покрыть всё. Сосредоточьтесь на критических путях: регистрация, вход, оформление заказа, оплата.

Также полезно знать, какие методы Codeception чаще всего используются. Вот небольшая таблица:

Метод Что делает Пример
amOnPage Переходит на указанный URL $I->amOnPage('/site/index')
see Проверяет, что текст есть на странице $I->see('Hello')
fillField Заполняет поле формы $I->fillField('Email', 'test@test.com')
click Кликает по элементу $I->click('Submit')
seeInDatabase Проверяет запись в БД $I->seeInDatabase('users', ['email' => 'test@test.com'])

Эти методы покроют 80% ваших задач. Остальное — дело практики.

Что в итоге?

Тестирование с Codeception в Yii2 — это не роскошь, а необходимость для любого серьёзного проекта. Оно помогает быстрее находить ошибки, не бояться рефакторинга и увереннее выпускать релизы. Да, поначалу придётся потратить время на настройку и написание первых тестов. Но потом вы скажете себе спасибо, когда тест поймает баг, который сэкономит вам несколько часов дебага.

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

Если вы хотите, чтобы ваш проект был качественным и без багов, но нет времени разбираться самостоятельно — обращайтесь в студию WNDER. Мы поможем с разработкой и тестированием.

Удачи в тестах!

Студия WNDER