Введение

Важный этап при разработке приложения - это тестирование его работоспособности. Заниматься этим вручную хлопотно (особенно после каждого изменения прогонять все тесты заново) да и можно что-то пропустить. Поэтому нужен инструмент для автоматического тестирования. Как вариант - это unit-тесты. Они хороши для тестирования отдельных элементов скрипта. Но чтобы проверить функциональность всего приложения вцелом, взаимосвязь всех компонентов и модулей, нужен более серьезный инструмент. Такими возможностями обладает Codeception. Поэтому именно о нем пойдет речь в сегодняшней статье.

Codeception - это фреймворк для тестирования на основе PHPUnit и Mink. Имеет три вида тестов: модульные (unit), функциональные (functional) и приемочные (acceptance). В комплекте есть модули для тестирования различных фреймворков и API (поддерживает стандарты REST и SOAP). Также может формировать HTML и XML отчёты о тестировании.

Установить Codeception можно через Composer, Git или скачав phar-архив.

Установка через Composer

Создаем файл composer.json:

{
    "name": "project name",
    "require": {
        "codeception/codeception": "*"
    }
}

Сохраняем файл и выполняем команду composer install. После загрузки инизиализируем Codeception командой:

$ ./vendor/bin/codecept bootstrap

Установка через Git

Клонируем репозиторий с GitHub и установливаем зависимости:

$ git clone git://github.com/Codeception/Codeception.git
$ cd Codeception
$ curl -s http://getcomposer.org/installer | php
$ php composer.phar install

Инизиализируем Codeception с указанием пути к каталогу проекта:

$ php codecept bootstrap /path_to_project

Установка codecept.phar

Скачиваем phar-архив:

$ wget http://codeception.com/codecept.phar

Инициализируем командой:

$ php codecept.phar bootstrap

Модульные тесты

Сначала для удобства установим расширение Verify. Которое сделает код наших тестов более читаемым. Используем для этого Composer:

$ composer require "codeception/verify:*"

После добавления создаем базовые классы тестов:

$ ./vendor/bin/codecept build

Здесь стоит заметить что у Codeception есть команда для генерации тестов. Давайте ей воспользуемся - создадим файл для тестирования модели продукта:

$ ./vendor/bin/codecept generate:phpunit unit Product

Теперь у нас есть файл tests/unit/ProductTest.php с которым мы можем работать как PHPUnit тестом. Например:

class ProductTest extends \PHPUnit_Framework_TestCase
{

    protected $product;

    protected function setUp()
    {
        $this->product = new Product();
    }

    protected function tearDown()
    {
        $this->product = null;
    }

    /**
     * Проверка на обязательные поля.
     */
    public function testRequiredFields()
    {
        $this->product->name = '';
        $this->product->price = '';

        verify($this->product->save())->false();

        $this->product->name = 'имя продукта';
        $this->product->price = 5000;

        verify($this->product->save())->true();
    }

    /**
     * Проверка на получение категории.
     */
    public function testGetCategory()
    {
        $category = $this->product->getCategory();

        verify($category)->isInstanceOf('Category');

        verify($category->id)->equals(1);
    }

}

Запускаем модульные тесты командой:

$ ./vendor/bin/codecept run unit

При успешном выполнении увидим такой результат:

Unit Tests (2) ------------------------------------
✔ ProductTest: Required fields (0.05s)
✔ ProductTest: Get category (0.02s)
---------------------------------------------------

Time: 934 ms, Memory: 11.50MB

OK (2 tests, 4 assertions)

Функциональные тесты

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

С полным списком модулей можно ознакомится на GitHub или официальном сайте. Если в списке нет подходящего модуля можно интегрировать свой фреймворк. Делается это созданием моста между BrowserKit (его использует Codeception) и нашим приложением.

Мы будем использовать собственный модуль Gateway. Этот модуль подойдет если наш фреймворк использует Symfony HttpKernel.

Сначала создаем сам модуль по адресу tests/modules/Gateway.php:

namespace Codeception\Module;

use Codeception\TestCase;
use Symfony\Component\HttpKernel\Client;

class Gateway extends \Codeception\Lib\Framework 
{

    public function _initialize()
    {
        require dirname(dirname(__DIR__)) . '/vendor/autoload.php';

        $config = require dirname(__DIR__) . '/config/main.php';
        $this->kernel = new \app\framework\Frontend($config);
    }

    public function _before(TestCase $test)
    {
        $this->client = new Client($this->kernel);
        $this->client->followRedirects(true);
    }

    public function isStatusCode($code)
    {
        return $this->kernel->handle()->getStatusCode() === $code;
    }
    
}

Затем подключаем его в файле конфигурации (tests/functional.suite.yml):

class_name: FunctionalTester
modules:
    enabled: [Gateway, \Helper\Functional]

Пересоздаем базовые классы тестов:

$ ./vendor/bin/codecept build

Теперь можем приступать к написанию тестов.

С помощью генератора добавим тест для страницы авторизации:

$ ./vendor/bin/codecept generate:cept functional AuthPage

В созданном файле (tests/functional/AuthPageCept.php) пишем следующий код:

$I = new FunctionalTester($scenario);
$I->amOnPage('/');
$I->click('Авторизация');
$I->fillField('username','Admin');
$I->fillField('password','1q2w3e');
$I->click('Войти');
$I->see('Добро пожаловать, Admin', 'h1');

Запускаем функциональные тесты командой:

$ ./vendor/bin/codecept run functional

При успешном выполнении увидим такой результат:

Functional Tests (1) ------------------------------
✔ AuthPageCept:  (0.03s)
---------------------------------------------------

Time: 165 ms, Memory: 14.25MB

OK (1 test, 1 assertion)

Приемочные тесты

Приемочные тесты расчитаны на то, что их создавать и выполнять может не только программист. А, например, тестировщик, менеджер или даже заказчик. Основное требование - это наличие браузера. И доступность приложения по указанному в конфигурации адресу.

Можно использовать как PHP-браузер, так и обычный браузер через Selenium. Мы остановимся на PHP-браузере. Указываем об этом в файле tests/acceptance.suite.yml:

class_name: AcceptanceTester    
modules:
   enabled: [PhpBrowser, \Helper\Acceptance]
   config:
       PhpBrowser:
            url: 'http://localhost:8000'

Пересоздаем базовые классы тестов:

$ ./vendor/bin/codecept build

Этот модуль требует доступности приложения по адресу http://localhost:8000. Если это условие соблюдено можем приступать к тестам.

Структура и стиль написания идентичны функциональным тестам. Поэтому за пример возмем тот же тест страницы авторизации, который использовали при функциональном тестировании.

Последовательность действий такая же. Сначала генерируем тест:

$ ./vendor/bin/codecept generate:cept acceptance AuthPage

Потом заполняем его (это файл tests/acceptance/AuthPageCept.php):

$I = new AcceptanceTester($scenario);
$I->wantTo('Проверить авторизацию на сайте.');
$I->amOnPage('/');
$I->click('Авторизация');
$I->fillField('username','Admin');
$I->fillField('password','1q2w3e');
$I->click('Войти');
$I->see('Добро пожаловать, Admin', 'h1');

И запускам тесты:

$ ./vendor/bin/codecept run acceptance

При успешном выполнении увидим такой результат:

Acceptance Tests (1) ---------------------------------------------------
✔ AuthPageCept: Perform actions and see result (0.01s)
---------------------------------------------------

Time: 168 ms, Memory: 12.25MB

OK (1 test, 0 assertions)

Настройка покрытия кода

Для формирования HTML и XML отчётов включаем утилиту coverage в файле codeception.yml и указываем какие файлы должны попасть в отчет:

coverage:
    enabled: true
    whitelist:
        include:
            - app/*
        exclude:
            - app/commands/*
            - app/config/*
            - app/migrations/*
    blacklist:
        include:
            - app/migrations/*
        exclude:
            - app/services/Sendler.php

Где whitelist - это перечень файлов которые будут включены в отчет (даже если не затронуты), а blacklist - это перечень файлов которые не будут включены в отчет (даже если затронуты). Опции include и exclude здесь добавляют или удаляют файлы.

Запускаем тесты с формированием отчетов:

$ ./vendor/bin/codecept run --coverage --html --xml

После успешного выполнения тестов мы видим что-то подобное:

- HTML report generated in file:///home/user/projects/projectName/tests/_output/report.html
- XML report generated in file:///home/user/projects/projectName/tests/_output/report.xml

Это адреса файлов с отчетами тестирования. Можем перейти и посмотреть на них. Но если мы хотим посмотреть отчет о покрытии тестами файлов проекта, тогда запускаем тесты так:

$ ./vendor/bin/codecept run --coverage-html

А файл с отчетом смотрим по адресу projectName/tests/_output/coverage/index.html.

Отладка тестов

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

$ ./vendor/bin/codecept run --debug

Что касается модульных тестов то, например, нужно узнать что за объект получен, а функция var_dump() игнорируется. Даже когда параметр --debug указан. В этом случае у Codeception есть своя функция отображения структурированной информации:

\Codeception\Util\Debug::debug('текст или массив');

Заключение

Таким образом с помощью Codeception мы смогли не только протестировать отдельные классы проекта, но также проверить взаимосвязь всех его компонентов и корректную работу в браузере. А благодаря утилите coverage увидеть какие части приложения протестированы, а какие нет.