Введение
Codeception — это популярный фреймворк для тестирования веб-приложений. Он написан поверх PHPUnit и позволяет более элегантно писать тесты используя методологию BDD.
Поддерживает три вида тестирования:
Unit tests (модульные тесты) — тестирование отдельных php-классов.
Functional tests (функциональные тесты) — тестирование приложения без использования веб-сервера и без поддержки javascript.
Acceptance tests (приёмочные тесты) — тестирование с использованием браузера, имитирует действия пользователя, поддерживает javascript. Этот вид тестирования интересует нас в первую очередь.
Пример приёмочного теста:
$I->amOnPage('/user');
$I->fillField('name', 'admin');
$I->fillField('pass', 'qwerty');
$I->click('Log in');
$I->see('admin', 'h1');Довольно просто и наглядно.
Приёмочные тесты могут запускаться двумя способами:
Модуль PhpBrowser — эмулятор браузера на php, использующий Guzzle и Symfony BrowserKit. Выполняется быстро, но не поддерживает javascript.
Модуль WebDriver — полноценный браузер Chrome, который может работать в headless (невидимом) режиме.
Установка и настройка
Ставим Codeception:
composer require codeception/codeception --devИнициализируем:
vendor/bin/codecept bootstrap
В процессе скрипт попросит разрешение на выполнение composer update и нужно согласиться, иначе всё сломается.
После инициализации в корне появится файл codeception.yml и папка tests, а в composer.json добавится два пакета — codeception/module-phpbrowser и codeception/module-asserts.
В файле tests/Acceptance.suite.yml изменяем опцию url на адрес тестируемого сайта:
actor: AcceptanceTester
modules:
enabled:
- PhpBrowser:
url: https://example.comВсе файлы и папки, связанные с unit и functional тестами можно удалить.
Первый тест на PhpBrowser
В папке tests/Acceptance создаём php файл с названием теста и суффиксом Cest, например FrontPageCest.php:
<?php
namespace Tests\Acceptance;
use Tests\Support\AcceptanceTester;
class FrontPageCest {
public function testFrontPage(AcceptanceTester $I) {
$I->amOnPage('/');
$I->seeElement('body.path-frontpage');
}
}Cest — это формат тестов, в котором каждый тест это публичный метод класса. Есть ещё старый формат cept, где тест это просто php файл с набором команд.
Для запуска всех тестов выполняем:
vendor/bin/codecept runЗапуск одного cest файла:
vendor/bin/codecept run acceptance FrontPageCestЗапуск конкретного cest теста:
vendor/bin/codecept run acceptance FrontPageCest:testFrontPageСкриншот работы Codeception:
Документация по запуску тестов.
Документация по модулю PhpBrowser.
Создание тестового окружения
Как только тесты буду изменять данные на сайте (создавать/удалять ноды, отправлять формы, заливать файлы) встанет необходимость отдельного тестового окружения, т.е. своей БД, своей директории под загружаемые файлы и своих настроек.
Здесь вариантов несколько, но самый простой это стандартный мультисайтинг, тем более по такому же принципу работают друпаловские тесты:
-
Создаём папку
sites/test -
Копируем туда
settings.phpиservices.ymlизsites/default -
В
sites/test/settings.phpизменяем$settings['file_public_path'],$settings['file_private_path']и$settings['file_temp_path']если они указаны. -
Клонируем БД и прописываем данные новой базы в
sites/test/settings.php. -
Создаём отдельный домен и направляем его в тестируемый сайт.
-
Переименовываем файл
sites/example.sites.phpвsites/sites.phpи добавляем в конец:$sites['test.example.com'] = 'test';Где вместо
test.example.comпишем название своего тестового домена.
Теперь при открытии http://test.example.com друпал будет брать настройки из sites/test/settings.php и заливать файлы в sites/test/files вместо sites/default/files.
Чтобы каждый раз перед старом теста вручную не клонировать базу поможет модуль Db:
-
Ставим модуль:
composer require codeception/module-db --dev -
Правим
tests/Acceptance.suite.yml:actor: AcceptanceTester modules: enabled: - PhpBrowser: url: https://test.example.com - Db: dsn: 'mysql:host=localhost;dbname=testing' user: 'root' password: 'root' dump: 'tests/_data/dump.sql' populate: true cleanup: false reconnect: false waitlock: 0 populator: 'vendor/bin/drush sql-dump --result-file=$dump && mysql --user=$user --password=$password --host=$host $dbname < $dump'
Теперь при каждом запуске тестов codeception будет с помощью drush делать актуальный дамп в tests/_data/dump.sql, очищать базу testing и импортировать туда данные из дампа.
Важное замечание — на Windows во всех файловых путях надо использовать обратный слэш \, т.е.:
— не vendor/bin/drush, а vendor\bin\drush
— не tests/_data/dump.sql, а tests\_data\dump.sql
Здесь все примеры написаны для Linux с использованием /, поэтому надо быть внимательным!
Тестирование в реальном браузере Chrome
Для тестирования в реальном браузере понадобится модуль WebDriver, который реализует общение между codeception и браузером по одноимённому протоколу WebDriver.
Модуль умеет запускать тесты с помощью Selenium, ChromeDriver, PhantomJS, SauceLabs, BrowserStack и TestingBot. Самый простой способ это ChromeDriver — независимый сервер реализующий протокол WebDriver для хрома, сервер разрабатывается командой Chromium, может работать как самостоятельно, так и как часть Selenium WebDriver.
Ставим модуль:
composer require codeception/module-webdriver --devСкачиваем ChromeDriver для своей ОС и установленной версии Chrome (да, после каждого мажорного обновления Chrome придётся обновлять ChromeDriver). Распаковываем. Добавляем путь к бинарнику в PATH.
Изменяем Acceptance.suite.yml:
actor: AcceptanceTester
modules:
enabled:
- WebDriver:
url: 'http://test.example.com/'
window_size: false
port: 9515
browser: chrome
capabilities:
'goog:chromeOptions':
args: []
- Db:
dsn: 'mysql:host=localhost;dbname=testing'
user: 'root'
password: 'root'
dump: 'tests/_data/dump.sql'
populate: true
cleanup: false
reconnect: false
waitlock: 0
populator: 'vendor/bin/drush sql-dump --result-file=$dump && mysql --user=$user --password=$password --host=$host $dbname < $dump'
extensions:
enabled:
- Codeception\Extension\RunProcess:
- 'chromedriver --url-base=/wd/hub --port=9515'Теперь тесты будут выполнятся в реальном браузере Chrome.
Методы для модуля WebDriver немного отличаются от PhpBrowser, например see() ищет только видимые элементы, и если элемент скрыт с помощью display:none; то тест выдаст ошибку. Так же в WebDriver есть методы, отсутствующие в PhpBrowser, например seeElementInDOM(), seeInPopup(), wait(), executeJS() и т.д.
Документация по модулю WebDriver.
Лайфхаки
Очистка тестового окружения перед стартом теста
extensions:
enabled:
- Codeception\Extension\RunBefore:
- 'vendor/bin/codecept clean'
- 'rm -rf sites/test/files/*'Отключение drupal модулей мешающих тестированию
extensions:
enabled:
- Codeception\Extension\RunBefore:
- 'vendor/bin/drush pm-uninstall captcha backup_migrate automated_cron --uri=test.example.com -y'Смена пароля администратора в тестовом окружении
extensions:
enabled:
- Codeception\Extension\RunBefore:
- 'vendor/bin/drush user-password admin qwerty --uri=test.example.com'Просмотр логов после неудачного выполнения теста
После краха теста в папке tests/_output будет html код страницы и сриншот. Самостоятельно сохранить код можно с помощью $I->makeHtmlSnapshot(), а скриншот с помощью $I->makeScreenshot().
Вынос часто повторяющихся действий в свой метод
Код можно выносить в файл tests/Support/AcceptanceTester.php. Например аутентификация:
class AcceptanceTester extends \Codeception\Actor {
use _generated\AcceptanceTesterActions;
/**
* Login as $username.
*/
public function login($username, $password) {
$this->amOnPage('/user');
$this->fillField('name', $username);
$this->fillField('pass', $password);
$this->click('#edit-submit');
}
}Использование в cest файле:
public function testName(AcceptanceTester $I) {
$I->login('admin', 'qwerty');
...
}Запросы в тестовую базу данных
Добавляем в tests/Support/Helper/Acceptance.php:
/**
* Sql query.
*/
public function sqlQuery($query){
return $this->getModule('Db')->_getDbh()->query($query);
}
Добавляем в Acceptance.suite.yml:
modules:
enabled:
...
- \Helper\AcceptanceВыполняем в консоли:
vendor/bin/codecept buildИспользование в cest файле:
$last_inserted_nid = $I->sqlQuery("SELECT MAX(nid) FROM node")->fetchColumn();Запуск тестов из PhpStorm
Документация на сайте JetBrains.
Пример настройки для Windows:
Очерёдность запуска тестов
Тесты в рамках cest файла выполняются в порядке следования и на это повлиять нельзя. Сами cest файлы выполняются по алфавиту и на это тоже повлиять нельзя, кроме как добавлять в название порядковый номер:
Test01FooCest.php
Test02BarCest.php
Test03BazCest.phpПродвинутый amOnPage()
У меня вместо amOnPage() есть хелпер amOnDrupalPage(), открывающий страницу и проверяющий её на наличие друпаловских ошибок:
tests/Support/AcceptanceTester.php
/**
* Goto to page and check errors.
*/
public function amOnDrupalPage($url) {
if (strpos($url, '://') === FALSE) {
$this->amOnPage($url);
}
else {
$this->amOnUrl($url);
}
$this->seeElementInDOM('body');
$this->dontSeeElement('.messages--error');
$this->seeNumRecords(0, 'watchdog', ['type' => 'php']);
}Выполнить js и получить результат
$currentPath = $I->executeJS("return drupalSettings.path.currentPath;");Получить адреса всех ссылок в блоке
$urls = $I->grabMultiple('.block-name a', 'href');Хелпер для проверки хлебных крошек
// tests/Support/AcceptanceTester.php
/**
* See breadcrumb.
*/
public function seeBreadcrumb(array $items) {
foreach ($items as $key => $text) {
$this->see($text, '.breadcrumb li:nth-child(' . ($key+1) . ')');
}
}Использование:
$I->amOnPage('/catalog/auto');
$I->seeBreadcrumb(['Главная', 'Каталог', 'Авто-товары']);Использование функций assert* в тестах
Включаем модуль Asserts:
# Acceptance.suite.yml
modules:
enabled:
...
- AssertsДелаем билд:
vendor/bin/codecept buildИспользуем:
class SiteCest {
public function testFirst(AcceptanceTester $I): void {
$result = 2*2;
$I->assertEquals(4, $result);
}
}Вообще рекомендую пробежаться по официальной документации, чтобы понять возможности Codeception.
Комментарии
Спасибо, как всегда понятно и доходчиво.
я на WebDriver писал, и признаться мне очень надоело базу востанавливать
Спасибо, полезная статья.
На очередность выполнения тестов можно влиять с помошью директивы @depends в анотации к методу https://codeception.com/docs/07-AdvancedUsage#Dependencies
Работает в том числе и для задания очередности выполнения классов (cest файлов).
Добавить комментарий