Тестирование веб-приложений с помощью Codeception

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

Платформа тестирования является готовой инфраструктурой для ваших тестов. И когда я говорю тесты, я не имею ввиду только модульные тесты (юнит-тесты). Мыслите шире. Приложение также нуждается в проверке в браузере с помощью Selenium (или другого инструмента). Так что вам нужны: модульные тесты, Selenium тесты, вероятно тесты API, и все они должны быть выполнены вместе. Я бы хотел представить вам Codeception – это современный PHP фреймворк для тестирования, построенный на Symfony2 компонентах, Mink, и PHPUnit.

Codeception включает в себя более 20 модулей для тестирования наиболее популярных PHP фреймворков, API (REST, SOAP), хранилище данных (MySQL, PostgreSQL, MongoDB, и т.д.) и проверку в браузере с помощью Selenium WebDriver. Посетите официальный сайт, чтобы посмотреть какие есть ещё модули: codeception.com

И какой лучший способ для запуска всех этих тестов, на сервере с непрерывной интеграцией, после каждого коммита? Я рекомендую использовать Codeception в качестве основного блока для вашей платформы автоматизации тестирования; Codeception может выполнять вместе Selenium тесты, API тесты, и PHPUnit тесты и создавать подробные HTML и XML отчёты.

Начать писать тесты с Codeception довольно легко. Вы можете научить вашего QA специалиста писать такие тесты используя только базовые знания PHP. Основы Codeception оставляю для самостоятельного изучения, а рассказать хочу о том, как Codeception может помочь вам построить лучшую платформу автоматизации тестирования.

Рой тестов

Недостаточно сказать, “Мы пишем тесты” или “Мы практикуем TDD/BDD”. То, к чему вы должны стремиться, является создание твердой платформы для автоматизации тестирования. И не имеет значения, пишете ли вы тесты перед кодом, или код перед тестами, или тесты описываются как история (в стиле BDD). Что действительно важно, это то, как вы управляете всеми этими тестами.

Со временем у вас может быть больше чем 1,000 или 10,000 или 100,000 тестов, и они могут работать в течение трех или четырех часов. Вам нужно сохранять код для ваших тестов чистым и простым. Тесты должны легко рефакториться (так как они постоянно меняются, потому что меняется само приложение) и они должны легко расширяться. В принципе, при написании тестирующего кода, вы должны следовать тем же лучшим практикам, которые используете для написания рабочего кода, .

Базовый тест

Вот пример приёмочного теста в Codeception. В этом сценарии я посылаю другу инвайт на сайт. Чтобы проверить работает ли этот функционал, мне нужно войти в систему, сгенерировать код приглашения, а затем попытаться зарегистрировать нового пользователя с этим кодом. Вот как сценарий может выглядеть в Codeception:

~~~{.php} <?php

$I = new WebGuy($scenario); $I->wantTo(‘зарегистрировать код приглашения и зарегистрироваться с помощью него’); $I->amOnPage(‘/login’); $I->fillField(‘Username’,’tester1′); $I->fillField(‘Password’,’123456′); $I->click(‘Вход’); $I->see(‘Привет, tester1’); $I->click(‘Invite’); $I->see(‘Вставьте свой инвайт код’); $code = $I->grabTextFrom(‘#invitation_code’); $I->click(‘Выход’); $I->amOnPage(‘/register’); $I->see(‘Вы можете зарегистрироваться, если у вас есть код приглашения!’, ‘h1’); $I->fillField(‘Код приглашения’, $code); $I->fillField(‘Username’,’tester2′); $I->fillField(‘Password’,’654321′); $I->click(‘#invite_form input[type=submit]’); $I->see(‘Привет, tester2’);


Даже если вы не знакомы с Codeception, этот тест должен быть легко читаемым; все действия описаны с точки зрения пользователя и никаких специальных технических терминов здесь не используются.  Этот тест может выглядеть простым и чистым, но если у вас есть подобные тесты, то вы должны их порефакторить. И вот почему…
#### Паттерн проектирования: Page Object
Вы должны избегать жестко закодированных значений в тестах,  так вы можете сделать тесты более устойчивым к изменениям. Предположим, подобные данные для формы входа и формы регистрации используются в других тестах. Если поле Имя пользователя было переименовано в “Login”, то вы должны были бы переписать тесты, использующие данные для входа. Мы можем использовать паттерн **Page Object** и **CSS/XPath**, чтобы найти элементы на странице. То же самое касается страницы приглашения.
**Page Object** является объектом, который представляет собой страницу (или несколько типичных страниц) вашего сайта, он может быть записан в виде класса со статическими свойствами и методами.
~~~{.php}
<?php
class InvitePage
{
    // URL страницы
    public static $URL = '/register';
   // Эти свойства определяют отображение пользовательского интерфейса для страницы регистрации
    public static $codeField = "Код приглашения";
    public static $usernameField = "Имя пользователя";
    public static $passwordField = "Пароль";
    public static $submitButton = "#invite_form input[type=submit]";
}

Теперь часть теста можно переписать таким образом:

~~~{.php} <?php

$I->amOnPage(InvitePage::$URL); $I->fillField(InvitePage::$codeField, $code); $I->fillField(InvitePage::$usernameField,’tester2′); $I->fillField(InvitePage::$passwordField,’654321′); $I->click(InvitePage::$submitButton);


В этом случае, все тесты посещения страницы Пригласить используют те же переменные для размещения элементов на странице. Если любой элемент меняет свое положение, вы можете обновить класс **InvitePage** по мере необходимости, не касаясь самих тестов.
#### Паттерн: Controller
Мы убрали некоторые жестко закодированные переменные из теста, но что произойдет, если вы добавите двух-факторную аутентификации в ваше веб-приложение. Что это значит для ваших тестов? Это означает, что вам нужно обновить все тесты, которые включают авторизацию юзера. Напомню: могут быть тысячи тестов. Было бы логично переместить некоторую логику в класс, так мы можем использовать сценарии входа/выхода/регистрации в других тестах.
Я спросил некоторых друзей инженеров по автоматизации о том, как они относятся к такого рода классам. Они используют термин **Step Object** (который является основным в BDD), но я предпочел бы называть его **Controller**  из-за сходства  с контроллерами, которые мы используем в веб-приложениях. Вот пример контроллера, который я хотел бы использовать для Codeception теста:
~~~{.php}
<?php
class UserController
{
    protected $user;
    public function __construct(WebGuy $I)
    {
        $this->user = $I;
    }
    public function login($username, $password)
    {
        $this->user->amOnPage(LoginPage::$URL);
        $this->user->fillField(LoginPage::$usernameField, $username);
        $this->user->fillField(LoginPage::$passwordField, $password);
        $this->user->click(LoginPage::$submitButton);
        $this->user->see("Hello, $username");
    }
    public function logout()
    {
        $this->user->click("Logout");
    }
    public function registerWithInviteCode($code, $username, $password)
    {
        $this->user->amOnPage(InvitePage::$URL);
        $this->user->fillField(InvitePage::$codeField, $code);
        $this->user->fillField(InvitePage::$usernameField, $username);
        $this->user->fillField(InvitePage::$passwordField, $password);
        $this->user->click(InvitePage::$submitButton);
    }
}

Теперь код имеет логические блоки и сценарий будет выглядеть намного чище:

~~~{.php} <?php

$I = new WebGuy($scenario); $U = new UserController($I); $I->wantTo(‘generate invitation code and sign up using it’); $U->login(‘tester1’, ‘123456’); $I->click(‘Invite’); $I->see(‘Copy this invitation code’); $code = $I->grabTextFrom(‘#invitation_code’); $U->logout(); $U->registerWithInviteCode($code, ‘tester2’, ‘654321’); $I->see(‘Hello, tester2’);


Вводя контролёры и объекты страниц, мы избавились от большинства жестко закодированных значений в основных шагах сценария. Это позволяет пользователям логиниться в других тестах, и это действительно полезно, так как многое из функциональности приложения доступно только для зарегистрированных пользователей. Вы можете написать 10 или 100 или даже 1000 тестов, но с использованием объектов страницы и контроллёров они не будут создавать беспорядок и их будет легко поддерживать.
### Отчёт для начальника
Из-за рефакторинга, тестовый код, вероятно, потерял часть своей читаемости для не-разработчиков. К счастью, Codeception может помочь с этим тоже. Codeception покажет вам все шаги сценария, которые были выполнены при выполнении теста:
~~~{.sh}
$ ./codecept.phar run acceptance --steps
Codeception PHP Testing Framework v1.5.5
Powered by PHPUnit 3.7.14 by Sebastian Bergmann.
Suite acceptance started
Trying to generate invitation code and sign up using it (InviteCept.php)
Scenario:
* I am on page '/login'
* I fill field 'Username','tester1'
* I fill field 'Password','123456'
* I click 'Sign In'
* I see 'Hello, tester1'
* I click 'Invite'
* I see 'Copy this invitation code'
* I grab text from '#invitation_code'
* I click 'Logout'
* I am on page '/register'
* I see 'You can sign up if you have invitation code!', 'h1'
* I fill field 'Invitation Code', 'iuSia89ak'
* I fill field 'Username','tester2'
* I fill field 'Password','654321'
* I click '#invite_form input[type=submit]'
* I see 'Hello, tester2'
Time: 10 seconds, Memory: 26.00Mb
OK (1 test, 4 assertions)

В этом случае на выходе тест будет читаемым. Этот сценарий может быть даже показан в не-техническому специалисту, менеджеру, бизнес-аналитику, и они будут понимать, что происходит. Codeception позволяет генерировать HTML-отчеты, написанные в подобной манере. Они могут быть показаны менеджерам или бизнес-аналитикам, чтобы продемонстрировать, что и как работает в вашем приложении.

Заключение

В настоящее время инженеры автоматизации тестирования предпочитают Java и Ruby, но так как вы PHP разработчик, вы, несомненно, заинтересованы в тестировании платформ, которые хорошо взаимодействуют с вашим PHP кодом и которая сама написана на PHP. Codeception представляет собой прекрасный инструмент с которым можно легко начать приёмочное тестирование, и является достаточно гибкими, чтобы создать прочную платформу автоматизации тестирования.

Но независимо от того, какой фреймворк вы используете, имейте в виду, что его не достаточно, чтобы написать грамотные тесты. Чтобы сделать тесты чистыми, стабильными, и удобными в сопровождении, используйте KISS и DRY принципы которые вы уже знаете. Паттерны проектирования такие как Page Object и Controller (Step Object) помогут вам в этом.