Разработка, HowTo

Часть 9: Codeception. Настройка. Unit-тесты (Тестирование ПО)

codeceptionОглавление

Продолжаем цикл статей Тестирование ПО. В этой части рассмотрим установку и настройку фреймворка codeception. Попутно перенесем все ранее написанные тесты на новую платформу.

Прежде, чем двигаться дальше нам необходимо познакомиться с таким фреймворком как codeception. На базе PHPUnit можно делать лишь блочные и модульные тесты. Его предназначение полностью расшифровывается в названии. Поскольку изначально он был заточен под применение исключительно для юнит-тестирования (и модульного конечно же).

PHPUnit никуда не делся, но так как найти абсолютно все ошибки лишь модульными тестами нельзя, то со временем стали появляться различные инструменты для проведения функционального или приемочного тестирования — одни из них это selenium, который открывает браузер при проверяет соответствие сгенерированной страницы том, что ожидает пользователь и имеет возможность заполнять формы и отправлять запросы.

Появилась потребность в инструментах более высокого уровня, которые могли бы позволить манипулировать всем этим разнообразием консольных команд, приложений и всем тем, что позволяет искать ошибки в приложениях. Одним из таких инструментов стал Codeception — надстройка над PHPUnit, которая помимо прочего умеет выполнять тесты в браузере, осуществлять сетевые запросы, заполнять формы и много чего еще.

Ранее выделялось четыре стадии тестирования проекта:

  • юнит-тестирование
  • интеграционное — оно же блочное
  • функциональное
  • приемочное

Было отмечено, что в рамках yii осуществлять чисто блочное тестирование не всегда возможно из-за его архитектурных особенностей.

Codeception несколько упрощает жизнь разработчика. В рамках этого инструмента существует три уровня изоляции тестов.

  • Юнит-тесты. Этот уровень изоляции сочетает в себе как стандартные юнит-тесты, так и блочные.
  • Функциональные тесты — это по-прежнему отправка данных в формы, проверка ответа сервера, тестирование внешнего api.
  • Приемочные тесты — запускается headless-браузер (окна не открывается, браузер работает в фоновом режиме) и фреймворк выполняет действия от лица пользователя: двигает мышкой, вводит текст в поля ввода, разгадывает капчу, переходит по пунктам меню. Эта часть тестов способна находить ошибки в коде браузерных приложений.

установка и инициализация

Нам потребуется пакет codeception/codeception — основная составляющая фреймворка.

composer require --dev "codeception/codeception:^2.0.0"

Конструкция означает, что мы ставим запрашиваем самую последнюю версию из ветки 2.x. Выполняем данную команду на виртуальной машине в папке /var/www.

Дальше стоит поставить codeception/verify — достаточно удобный пакет, который позволяет писать предикаты, близкие к естественному языку. Это лишь синтаксический сахар. Сравните два теста, которые описаны ниже — первый с применением стандартных предикатов, а второй — с синтаксическим сахаром.

$this->assertEquals($user->getName(), 'test');
verify($user->getName())->equals('test');

Ставится достаточно просто.

composer require --dev "codeception/verify:^0.3.0"

Последнее, что нам может потребоваться — это модуль codeception/specify. Еще одна порция синтаксического сахара. Чуть позже мы будем применять ее на практике.

composer require --dev "codeception/specify:^0.4.0"

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

Другой простой способ установить описанные выше компоненты — это добавить в секци require-dev несколько пакетов и выполнить провизию на машине.

"require-dev": {
  "codeception/codeception": "^2.0.0"
  "codeception/verify": "^0.3.0",
  "codeception/specify": "^0.4.0"
}

Интеграция Codeception и Yii2

Прежде чем мы приступим к освоению нового инструмента переименуем папку tests — поскольку это название закреплено за тестами codeception, а наши более ранние эксперименты предназначались для того, чтобы понять, как работают тесты изнутри.

Чтобы все прошло удачно необходимо, чтобы папка tests и файл codeception.yml в корне проекта отсутствовали. Иначе инициализация скажет, что инфраструктура уже развернута. Проверяем, что у нас нет ничего лишнего и запускаем из консоли команду на подготовку окружения.

vendor/bin/codecept bootstrap --ns common\\tests common

Каталог с тестами общими для всего проекта будет создан в папке common. Да, мы не будем дальше использовать вызовый через composer run из-за бага, который был описан ранее. Опция —ns для нас жизненно необходима для того, чтобы все генерируемые фреймворком в процессе работы файлы попадали в правильный неймспейс сразу (это файлы из каталога tests/_generated).

Так же в каталоге common будет создан файл codeception.yml — это конфигурация для запуска.

Изначально codeception не умеет дружить с yii2 полноценно. Поэтому нам требудется его этому научить. Один из параметров файла настроект указывает на _bootstrap.php — скрипт, который исполняется перед запуском тестов. Вот тут-то мы и выполним загрузку базовой части фреймворка в память.

Теперь добавим нужные для дальнейшей работы параметры в codeception.yml.

phpunit-yii-fail

Почему так происходит? Ранее мы указыали путь к тестовой конфигурации проекта в файлах настройки окружения. Так вот. TestCase из PHPUnit ничего про это окружение не знает, а следовательно он ничего не знает про инициализацию yii. Из этого следует, что объект \Yii::$app нам недоступен и мы не можем извлекать из него зависимости.

---------
7) UserTest: Password validation
 Test  tests/unit/model/UserTest.php:testPasswordValidation

  [PHPUnit_Framework_Exception] Trying to get property of non-object  

#1  /var/www/common/tests/unit/model/UserTest.php:77

Как быть? Отнаследоваться от \Codeception\Test\Unit. Этот класс уже знает, что нужно делать для запуска движка перед тестами. Если мы заглянем в исходники этого класса, то найдем там код, который и отвечает за инициализацию модулей (настройки берутся из массива part для каждого типа кейсов).

namespace common\tests\unit\model;

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

class UserTest extends Unit
{
    //....
}

Повторный запуск и все тесты завершаются корректно.

usertest-success

Перенос тестов DBUnit

У нас есть LoginFormTest, который спользует функционал DBUnit для добавления сущностей в базу данных. Как с ним быть? Все довольно просто — у yii есть механизм acrivefixture, который подмешивает в базу нужные данные и представляет из себя как раз таки иную реализацию того, что представляет из себя DBUnit.

Наиболее распространенная задача расширения для работы с базой из состава PHPUnit — загрузить данные (не будем рассматривать задачу валидации схем — редко кто из пользователей готовых фреймворков ею пользуется). Чтобы это сделать нам потребуется несколько простых вещей:

  • Проинициализироать класс фикстуры для нужной модели. В данном случае это common\fixtures\User
    namespace common\fixtures;
    
    use yii\test\ActiveFixture;
    
    class User extends ActiveFixture
    {
        public $modelClass = 'common\models\User';
    }
    
  • Создать хранилище, в котором будут содержаться данные фейковых пользователей (common/tests/_data/User.php)
    return [
        [
            'username' => 'test',
            'email' => 'test@test.test',
            'auth_key' => 'ssssssssssssssssssssssssssssssss',
            'password_hash' => '$2y$13$PP1EDCr7ujdhTxZT2DV96uM8e2rcdXHY1xAQINCIiB0gOck/VBwN6',
        ],
        [
            'username' => 'test1',
            'email' => 'test1@test.test',
            'auth_key' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
            'password_hash' => '$2y$13$PP1EDCr7ujdhTxZT2DV96uM8e2rcdXHY1xAQINCIiB0gOck/VBwN6',
        ]
    ];
  • Изменить порядок наследования класса LoginFormTest и отнаследовать его от Codeception/Test/Unit
  • Убрать все методы, которые были реализгованы для DBUnit
  • Реализовать метод _before, который содержит подгрузку фикстуры
        /**
         * Use user fixtures for database testing
         */
        public function _before()
        {
            $this->tester->haveFixtures([
                'user' => [
                    'class' => UserFixture::className(),
                    'dataFile' => codecept_data_dir() . 'user.php'
                ]
            ]);
        }
  • Доавить поддержку fixtures в настройки unit.suite.yml
    part: [orm, fixtures]
  • Помните мы добавляли сохранение модулей фреймворка которые мы оборачивали в mock’и? Этот функционал нужно убрать — настройки codeception по умолчанию инициализируют окружение yii законово ля каждого теста.

Все работает.

../vendor/bin/codecept run

loginform-success

Разделения тестов на фронтенд, бекенд и core-составляющие

Безусловно, можно сваливать все тесты в одну кучу (ядро, бек, фронт) и запускать их одной командой. Так, например сделано в yii2-standard-template. Но когда проект у нас сложный, то время выполнения тестов непрерывно растет и надо как-то разделять их на блоки, а для этого у нас есть прекрасная возможность — сама архитектура приложения этому способствует поскольку поделена на три части.

Для того, чтобы было удобно работать с тестами существует возможность глобального конфигурирования codeception — тесты каждой составляющей в своей папке, а глобальная конфигурация уровнем выше.

В нашем случае это файл codeception.yml в корне проекта.

# global codeception file to run tests from all apps
include:
    - common
    - frontend
    - backend
paths:
    log: console/runtime/logs
settings:
    colors: true

Разработчики шаблона о нас позаботились и заранее все сделали до нас. Мы лишь повторили их путь. И теперь можно запускать тесты уже и корня проекта. При таком способе будут выполнены все юнит и функиональные тесты для всех блоков системы.

Зачем все это?

После выполнения всех действия возникает вопрос: разработчики yii2-advanced-template уже все сделали до нас (положили нужные файлы в ынужные места, настроили окружение), зачем нам повторять этот путь? Конечно для того, чтобы знать как устроены подобные вещи изнутри. В следующих частях мы уже перейдем непосредственно к самим тестам, а сейчас мы только учимся, осваиваем технологию.

Домашнее задание

Самая главная проблема нашего кейса: мы тестировали функционал фронтенда внутри тестов, которые посвящены ядру системы (я про LoginForm). Целесообразно вынести эти тесты во frontend-составляющую, что вы и сделаете в качестве домашенй работы.

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

Одна из проблем с которой вы столкнетесь — это валидация password_reset_token. Суть в том, что срок жизни токена закодирован в самом токене. И на момент описания статьи этот токен уже истек. Подумайте как обойти эту проблему не использую рандомизированные данные. Если вы не сможете ничего придумать, то посмотрите в исходники к статье. Рекомендую посмотреть на метод User::isPasswordResetTokenValid().

В этой части мы говорим исключительно о юнит тестах. Поэтому не стоит сразу браться за исправление функциональных тестов — о них дальше. Да, безусловно, на реальном проекте оставлять поломанный код в системе контроля версий нельзя. Помните об этом! Мы это делаем только чтобы не сильно перегружать информацией каждую часть этого цикла!

Исходные тексты

Ссылки и документация

Реклама

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s