Введение
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.
- Как из PhpStorm выполнить тест(ы)
- Вставка в CKEditor видео из ВКонтакте и Rutube (расширение модуля CKEditor 5 Media Embed)
- Как работает опция "Aggregation type" в настройках полей Views при включённой агрегации
- Создание сравнительной таблицы с значениями из EAV Field
- Препроцессинг настроек форматтера перед сохранением
Комментарии
Спасибо, как всегда понятно и доходчиво.
я на WebDriver писал, и признаться мне очень надоело базу востанавливать
Спасибо, полезная статья.
На очередность выполнения тестов можно влиять с помошью директивы @depends в анотации к методу https://codeception.com/docs/07-AdvancedUsage#Dependencies
Работает в том числе и для задания очередности выполнения классов (cest файлов).
Добавить комментарий