Сервис на Yii2: Тестирование приложения с Codeception
Мы разбили проект на модули и сделали управление пользователями, так что некий каркас приложения у нас готов. Пора приступить к написанию модульных, интеграционных, функциональных и приёмочных тестов на Yii2. Сейчас расскажем о специфичных вещах, касающихся именно нашего проекта на основе нашего большого вебинара о тестировании.
Предыдущие части | Исходники на GitHub | Скринкаст о тестировании
Приятно при написании кода время от времени запускать автотесты и наблюдать, что весь код приложения работает корректно:
Codeception PHP Testing Framework v2.2.8 Powered by PHPUnit 5.7.9 by Sebastian Bergmann and contributors. Unit Tests (28) ------------------------------------------------------------------ ✔ ContactFormTest: Send email (1.05s) ✔ LoginFormTest: Login no user (0.42s) ✔ LoginFormTest: Login wrong password (0.03s) ... ✔ UserTest: Save new password (1.01s) ✔ UserTest: Scenarios | "admin update" (0.03s) ---------------------------------------------------------------------------------- Time: 7.03 seconds, Memory: 24.00MB OK (28 tests, 84 assertions)
Все вопросы по их структуре, нюансам написания, используемым инструментам и лучшим практикам мы рассмотрели в вебинаре о тестировании. Так что если вы не сталкивались с этим ранее, то вам сначала нужно посмотреть тот скринкаст.
Итак, начнём с банального.
Разделение конфигурации
По умолчанию в yii2-app-basic
нет разделения файлов конфигурации на общие и локальные. Имеется всего такой набор:
config ├── console.php ├── web.php ├── params.php ├── db.php ├── test.php └── test_db.php
Это неудобно при использовании систем контроля версий.
В первой части мы переработали файлы по примеру yii2-app-advanced
, добавив наследование конфигурации:
config ├── .gitignore ├── common.php ├── common-local.php ├── console.php ├── console-local.php ├── web.php ├── web-local.php ├── params.php └── params-local.php
чтобы в файле web/index.php
склеивать нужный комплект вместе:
$config = yii\helpers\ArrayHelper::merge( require(__DIR__ . '/../config/common.php'), require(__DIR__ . '/../config/common-local.php'), require(__DIR__ . '/../config/web.php'), require(__DIR__ . '/../config/web-local.php') );
Это избавило нас от необходимости копировать одни и те же секции в console.php
и web.php
путём переноса всего общего кода в common.php
.
Наш файл config/.gitignore
:
*-local.php
укажет Git-у игнорировать локальные файлы. В итоге мы можем делиться общими файлами и хранить персональные настройки в локальных.
Если вы не собираетесь делать аналогичное разделение, то оставьте всё как есть в стандартном приложении и перейдите к следующему разделу.
Неразделённой осталась только тестовая конфигурация:
config ├── ... ├── test.php └── test_db.php
где внутри файла test_db.php
вписаны настройки подключения к базе данных:
<?php $db = require(__DIR__ . '/db.php'); // test database! Important not to run tests on production or development databases $db['dsn'] = 'mysql:host=localhost;dbname=yii2_basic_tests'; return $db;
У каждого разработчика база будет своя. Нам нужно как-то вынести эти персональные настройки из под надзора системы контроля версий.
Для этого аналогично переделаем имеющуюся структуру:
config ├── ... ├── test.php └── test_db.php
на новую:
config ├── ... ├── test.php └── test-local.php
с разделением на общий test.php
(где обнулим параметр dsn
и добавим только отключения в тестах некоторых компонентов):
return [ 'id' => 'basic-tests', 'language' => 'en-US', 'components' => [ 'mailer' => [ 'useFileTransport' => true, ], 'urlManager' => [ 'showScriptName' => true, ], 'db' => [ 'dsn' => '', ], ], ];
и на локальный файл test-local.php
с кодом:
return [ 'components' => [ 'db' => [ 'dsn' => 'mysql:host=localhost;dbname=seokeys_test', ], ], ];
В итоге получим однообразную структуру:
config ├── .gitignore ├── common.php ├── common-local.php ├── console.php ├── console-local.php ├── web.php ├── web-local.php ├── test.php ├── test-local.php ├── params.php └── params-local.php
Осталось изменить все файлы, которые пока используют старую конфигурацию.
У нас это общие настройки модуля Yii2
в codeception.yml
:
actor: Tester paths: tests: tests log: tests/_output data: tests/_data helpers: tests/_support settings: bootstrap: _bootstrap.php memory_limit: 1024M colors: true modules: config: Yii2: configFile: 'config/test.php' cleanup: false
Удалим отсюда этот параметр configFile
.
Ещё это консольный файл tests/bin/yii
:
$config = yii\helpers\ArrayHelper::merge( require(__DIR__ . '/../../config/console.php'), [ 'components' => [ 'db' => require(__DIR__ . '/../../config/test_db.php') ] ] ); $application = new yii\console\Application($config);
Заменяем в нём эту загрузку параметров на новую склейку из нескольких путей:
$config = yii\helpers\ArrayHelper::merge( require(__DIR__ . '/../../config/common.php'), require(__DIR__ . '/../../config/common-local.php'), require(__DIR__ . '/../../config/console.php'), require(__DIR__ . '/../../config/console-local.php'), require(__DIR__ . '/../../config/test.php'), require(__DIR__ . '/../../config/test-local.php') ); $application = new yii\console\Application($config);
Далее для склейки и для удобства дополнительного конфигурирования каждого вида тестов добавим подпапку tests/_config
:
tests ├── ... └── _config ├── acceptance.php ├── functional.php └── unit.php
в acceptance.php
и unit.php
поместим данный код:
<?php return yii\helpers\ArrayHelper::merge( require(__DIR__ . '/../../config/common.php'), require(__DIR__ . '/../../config/common-local.php'), require(__DIR__ . '/../../config/web.php'), require(__DIR__ . '/../../config/web-local.php'), require(__DIR__ . '/../../config/test.php'), require(__DIR__ . '/../../config/test-local.php'), [ ] );
Внимания заслуживает functional.php
. В функциональных тестах точкой входа является корневая папка, из которой мы запускаем тесты, а не папка web
. Соответственно, псевдоним @webroot
автоматически выставится приложением на корневую директорию проекта.
Это чревато тем, что AssetManager в функциональных тестах будет публикавать скрипты не в ту папку, и загруженные файлы будут попадать не в web/uploads
. Путь псевдонима @webroot
мы не можем выставить напрямую в конфигурации, так как он заполняется на основе $request->getScriptFile()
:
namespace yii\web; class Application extends \yii\base\Application { ... protected function bootstrap() { $request = $this->getRequest(); Yii::setAlias('@webroot', dirname($request->getScriptFile())); Yii::setAlias('@web', $request->getBaseUrl()); parent::bootstrap(); } }
Поэтому в tests/_config/functional.php
мы можем дополнительно перенастроить параметр scriptFile
в request
:
<?php return yii\helpers\ArrayHelper::merge( require(__DIR__ . '/../../config/common.php'), require(__DIR__ . '/../../config/common-local.php'), require(__DIR__ . '/../../config/web.php'), require(__DIR__ . '/../../config/web-local.php'), require(__DIR__ . '/../../config/test.php'), require(__DIR__ . '/../../config/test-local.php'), [ 'components' => [ 'request' => [ 'scriptFile' => dirname(dirname(__DIR__)) . '/web/index-test.php', ], ], ] );
Теперь перестроим наши тестовые пакеты. Мы уже удалили секцию modules
из корневого codeception.yml
. Вместо неё индивидуально пропишем configFile
в tests/unit.suite.yml
:
class_name: UnitTester modules: enabled: - Asserts - Yii2: part: [orm, email] configFile: 'tests/_config/unit.php'
Аналогично в tests/functional.suite.yml
:
class_name: FunctionalTester modules: enabled: - Filesystem - Yii2: configFile: 'tests/_config/functional.php'
и tests/acceptance.suite.yml.example
:
class_name: AcceptanceTester modules: enabled: - WebDriver: url: http://127.0.0.1:8080/ browser: firefox - Yii2: part: orm entryScript: index-test.php configFile: 'tests/_config/acceptance.php'
После этого остался входной файл приёмочного тестирования web/index-test.php
и его заготовка environments/dev/web/index-tests.php
с кодом:
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); $config = require(__DIR__ . '/../config/test.php'); (new yii\web\Application($config))->run();
Заменим в нём строку с require
:
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); $config = require(__DIR__ . '/../tests/_config/acceptance.php'); (new yii\web\Application($config))->run();
Теперь нам нужно куда-то поместить образец файла config/test-local.php
, чтобы другие разработчики знали, как его заполнять. А ещё лучше сделать его автоматически появляющимся здесь при инициализации приложения.
Как мы помним, мы скопировали из yii2-app-advanced
скрипт init
с папкой environments
. Так что можно поместить заготовку нашего файла туда в подпапку окружения dev
:
environments ├── index.php ├── dev │ ├── config │ │ ├── common-local.php │ │ ├── console-local.php │ │ ├── params-local.php │ │ ├── test-local.php │ │ └── web-local.php │ ├── web │ │ ├── index.php │ │ └── index-test.php │ └── yii └── prod ├── config │ ├── common-local.php │ ├── console-local.php │ ├── params-local.php │ └── web-local.php ├── web │ └── index.php └── yii
В prod
-окружении тесты не запускают, так что туда эти данные помещать не нужно.
В итоге, при запуске команды:
php init
при установке приложения эти заготовки файлов автоматически скопируются на их исходные места.
Проверим правильность установки. Для этого можно создать тестовую базу, прописать её имя в своём config/test-local.php
и попробовать запустить миграции для того самого скрипта yii
:
php tests/bin/yii migrate
Если настройки базы указаны верно, то миграция создаст в ней такую же таблицу user
и свою вспомогательную migration
.
Именно так из двух скриптов
yii
иtests/bin/yii
можно автоматически обновлять рабочую и тестовую базы одними и теми же миграциями нашего приложения.
Запуск встроенных тестов
Попробуем запустить команду codecept
:
vendor/bin/codecept
Мы должны увидеть приветствие:
Codeception version 2.2.8
Мы можем попробовать запустить тесты по умолчанию из комплектации yii2-app-basic командой:
vendor/bin/codecept run unit
или:
vendor/bin/codecept run functional
но они у нас пока не сработают, так как мы переместили и переписали стандартные классы. Активируем сейчас запуск приёмочных и перепишем тесты.
Установка Selenium
По умолчанию доступны только unit- и functional-тесты. Для запуска приёмочных (acceptance) необходимо установить Selenium Standalone Server, который будет запускать для нас браузер Firefox или Google Chrome.
По умолчанию в стандартном composer.json
из yii2-app-basic устанавливается базовая версия codeception/base
пакета Codeception:
"require-dev": { "yiisoft/yii2-debug": "~2.0.0", "yiisoft/yii2-gii": "~2.0.0", "yiisoft/yii2-faker": "~2.0.0", "codeception/base": "^2.2.3", "codeception/verify": "~0.3.1", "codeception/specify": "~0.4.3" },
Она позволяет запускать модульные и функциональные тесты, но не содержит модуля для работы с приёмочными тестами через Selenium.
Заменим базовый пакет на полный запуском этих команд:
composer remove --dev codeception/base composer require --dev codeception/codeception
или вручную исправим composer.json
и запустим composer update
.
Второй на очереди – Selenium-сервер. Это .jar
-приложение. Для него, соответственно, нужно установить JRE от Java (но если у вас уже установлен редактор PhpStorm, то Java у уже есть).
С новой версии Selenium Server 3.0 драйверы взаимодействия с браузерами оказались вынесен в отдельные бинарники. Для Firefox выше версии 48 потребуется отдельно догрузить GeckoDriver и запускать сервер в командной строке вместе с ним:
java -jar -Dwebdriver.gecko.driver=/path/to/geckodriver /path/to/selenium-server-standalone-3.0.1.jar
После этого можно переименовать tests/acceptance.suite.yml.example
в tests/acceptance.suite.yml
, в новом окне консоли запустить свой сайт:
php yii serve
и пробовать позапускать приёмочные тесты:
vendor/bin/codecept run acceptance
С ними можно пока потренироваться на свежеустановленной копии yii2-app-basic.
Написание своих тестов
Для заполнения базы тестовыми наборами данных мы можем использовать так называемые фикстуры.
В новой папке tests/_fixtures
создадим класс UserFixture
с таким кодом:
namespace tests\_fixtures; use yii\test\ActiveFixture; class UserFixture extends ActiveFixture { public $modelClass = 'app\modules\user\models\User'; public $dataFile = '@tests/_fixtures/data/user.php'; }
и в подпапке tests/_fixtures/data
создадим файл user.php
с нашими наборами:
return [ [ 'username' => 'admin', 'auth_key' => 'eckb2DLY9uv6r1hM6D73eoHPvv6BfnXc', 'password_hash' => '$2y$13$D8areN6YSJh.fmR.Ww/sWOJ8EXRxNS9c7u7ubIrVozomTR8MY0PbO', 'password_reset_token' => null, 'email_confirm_token' => null, 'created_at' => '1439635619', 'updated_at' => '1439635619', 'email' => 'admin@example.com', 'status' => 1, ], ... ];
Далее для поддержки фикстур подключим часть fixtures
в секции part
для модуля Yii2
в unit.suite.yml
class_name: UnitTester modules: enabled: - Asserts - Yii2: part: [orm, fixtures, email] configFile: 'tests/_config/unit.php'
и acceptance.suite.yml
:
class_name: AcceptanceTester modules: enabled: - WebDriver: url: http://127.0.0.1:8080/ browser: firefox window_size: 1024x768 - Yii2: part: [orm, fixtures] entryScript: index-test.php configFile: 'tests/_config/acceptance.php'
Далее откроем tests/_bootstrap.php
и в самом низу добавим строку:
Yii::setAlias('@tests', __DIR__);
чтобы фреймворком корректно распознавались пути с псевдонимом @tests
и работала автозагрузка классов фикстур из пространства имён tests.
Теперь возьмем уже имеющиеся стандартные тесты, идущие в комплекте yii2-app-basic
, переделаем их под свои нужды и допишем свои.
Хорошая практика – раскладывать проверяющие классы по таким же папкам, где лежат оригинальные. Поэтому переместим всё как нам нужно:
project ├── tests │ ├── _config │ ├── _data │ ├── _fixtures │ │ ├── data │ │ │ └── user.php │ │ └── UserFixture.php │ ├── _output │ ├── _support │ │ ├── _generated │ │ ├── AcceptanceTester.php │ │ ├── FunctionalTester.php │ │ └── UnitTester.php │ ├── bin │ │ └── yii │ │ │ ├── acceptance │ │ ├── _bootstrap.php │ │ ├── admin │ │ │ └── HomeCest.php │ │ ├── _bootstrap.php │ │ ├── main │ │ │ ├── ContactCest.php │ │ │ └── HomeCest.php │ │ └── user │ │ ├── profile │ │ │ └── HomeCest.php │ │ ├── LoginCest.php │ │ ├── PasswordResetCest.php │ │ └── SignupCest.php │ │ │ ├── functional │ │ ├── _bootstrap.php │ │ ├── admin │ │ │ ├── HomeCest.php │ │ │ └── UsersCest.php │ │ ├── main │ │ │ ├── ContactCest.php │ │ │ └── HomeCest.php │ │ └── user │ │ ├── profile │ │ │ ├── HomeCest.php │ │ │ ├── PasswordChangeCest.php │ │ │ └── ProfileUpdateCest.php │ │ ├── LoginCest.php │ │ ├── PasswordResetCest.php │ │ └── SignupCest.php │ │ │ ├── unit │ │ ├── _bootstrap.php │ │ ├── _fixtures │ │ │ └── data │ │ │ ├── user-email-confirm.php │ │ │ ├── user-password-change.php │ │ │ └── user-password-reset.php │ │ └── modules │ │ ├── admin │ │ │ └── models │ │ │ └── UserTest.php │ │ ├── main │ │ │ └── forms │ │ │ └── ContactFormTest.php │ │ └── user │ │ ├── forms │ │ │ ├── EmailConfirmTest.php │ │ │ ├── ... │ │ │ └── SignupFormTest.php │ │ └── models │ │ └── UserTest.php │ │ │ ├── _bootstrap.php │ ├── acceptance.suite.yml │ ├── functional.suite.yml │ └── unit.suite.yml │ codeception.yml README.md
Код каждого файла можно посмотреть в исходниках на GitHub.
Нам практически всегда надо будет логиниться на сайт в функциональных и приёмочных тестах для проверки панели управления и личного кабинета пользователя. Каждый раз дёргать один и тот же код нентересно:
$I->amLoggedInAs($I->grabRecord(User::className(), ['username' => $username]));
Объекту $I
для удобства мы можем добавлять свои методы. Для этого откроем класс tests/_support/FunctionalTester.php
и допишем то, что нам пригодится:
class FunctionalTester extends \Codeception\Actor { use _generated\FunctionalTesterActions; public function amLoggedInAsAdmin() { $this->amLoggedInByUsername('admin'); } public function amLoggedInAsUser() { $this->amLoggedInByUsername('user'); } public function amLoggedInByUsername($username) { $I = $this; $I->amLoggedInAs($I->grabRecord(User::className(), ['username' => $username])); } }
Теперь мы можем просто вызывать $I->amLoggedInAsAdmin()
для быстрого входа в систему.
Аналогично добавляем аналогичные вещи в tests/_support/AcceptanceTester.php
:
class AcceptanceTester extends \Codeception\Actor { use _generated\AcceptanceTesterActions; public function amLoggedInAsAdmin() { $this->amLoggedInByCredentials('admin', 'adminpass'); } public function amLoggedInAsUser() { $this->amLoggedInByCredentials('user', 'userpass'); } public function amLoggedInByCredentials($username, $password) { $I = $this; $I->amOnPage(Url::to(['/user/default/login'])); $I->seeInTitle('Login'); $I->fillField('#loginform-username', $username); $I->fillField('#loginform-password', $password); $I->click('login-button'); $I->wait(2); $I->see('Profile', '.nav'); } }
Приёмочные тесты более высокоуровневые и не всегда нужны при наличии функциональных. Приёмочные полезны для проверки JavaScript и CSS. Клиентских скриптов у нас пока нет, так что пока их можно просто продублировать частично из функциональных с добавлением пауз для Selenium:
$I->wait(1);
после нажатия каждой кнопки.
Запуск тестов
После установки, если это наш новый проект, генерируем классы тестеров:
vendor/bin/codecept build
Дальше пробуем запустить сначала модульные и интеграционные из папки unit:
vendor/bin/codecept run unit
Успешный результат выглядит примерно так:
Codeception PHP Testing Framework v2.2.8 Powered by PHPUnit 5.7.9 by Sebastian Bergmann and contributors. Unit Tests (28) ------------------------------------------------------------------ ✔ ContactFormTest: Send email (1.05s) ✔ LoginFormTest: Login no user (0.42s) ✔ LoginFormTest: Login wrong password (0.03s) ... ✔ UserTest: Save new password (1.01s) ✔ UserTest: Scenarios | "admin update" (0.03s) ---------------------------------------------------------------------------------- Time: 7.03 seconds, Memory: 24.00MB OK (28 tests, 84 assertions)
И запустим функциональные:
vendor/bin/codecept run functional
и увидим похожий результат:
Codeception PHP Testing Framework v2.2.8 Powered by PHPUnit 5.7.7 by Sebastian Bergmann and contributors. Functional Tests (46) ------------------------------------------------------------ ✔ HomeCest: Test access (0.11s) ✔ HomeCest: Test home page (0.04s) ... ✔ ProfileUpdateCest: Update with wrong fields (0.06s) ✔ ProfileUpdateCest: Update success (0.07s) ---------------------------------------------------------------------------------- Time: 7.5 seconds, Memory: 52.00MB OK (46 tests, 82 assertions)
С виду здесь нет никакой разницы, но Codeception вместо создания объектов и вызова методов будет эмулировать запуск приложения, расставляя $_GET
, $_POST
и $_SERVER
так, как будто мы открываем страницы и отправляем POST-запросы.
Для запуска реальных приёмочных тестов нужно поднять тестовый сайт по адресу http://localhost:8080/index-test.php
. Для этого по подсказке инструкции в README.md
откроем ещё одну консоль и в ней запустим PHP-сервер:
yii serve
Он нам сообщит о своей готовности:
Server started on http://localhost:8080/ Document root is "/web" Quit the server with CTRL-C or COMMAND-C.
Откроем ещё одно окно консоли и запустим Selenium:
java -jar -Dwebdriver.gecko.driver=/path/to/geckodriver /path/to/selenium-server-standalone-3.0.1.jar
Увидим его приветствие:
12:58:15.076 INFO - Selenium build info: version: '3.0.1', revision: '1969d75' 12:58:15.077 INFO - Launching a standalone Selenium Server
И, запустив в первоначальной консоли:
vendor/bin/codecept run acceptance
мы должны увидеть на экране запущенный браузер, в котором будут сами открываться страницы, заполняться поля форм и нажиматься кнопки. При этом в консоли будет выводиться такой же как у функциональных результат:
Codeception PHP Testing Framework v2.2.8 Powered by PHPUnit 5.7.7 by Sebastian Bergmann and contributors. Acceptance Tests (11) ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ✔ HomeCest: Ensure that home page works (0.47s) ✔ HomeCest: Test access (0.28s) ... ✔ PasswordResetCest: Request successfully (2.64s) ✔ SignupCest: Signup successfully (3.89s) --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Time: 25.78 seconds, Memory: 18.00MB OK (11 tests, 21 assertions)
Если какой-либо тест завершится с ошибкой, то можно будет посмотреть дамп и скриншот ошибочной страницы в папке tests/_output
.
Анализ покрытия кода тестами
Этот анализ позволяет посмотеть, какие участки кода отработали в ходе тестирования. То есть оценить, весь ли код протестирован. Это производится за счёт сбора статистики запуска кода отладчиком XDebug.
В Ubuntu и Debian устанавливаем его стандартно из :
sudo apt-get install php5-xdebug
В Windows нужно скачать php_xdebug.dll
и подключить его в php.ini
:
[xdebug] zend_extension_ts=C:/php/ext/php_xdebug.dll
Но если в ваших тестах используется $this->specify(...)
, то будет постоянно вываливаться ошибка из-за ограничений уровня вложенности. Для решения проблемы нужно открыть php.ini
или /etc/php/conf.d/xdebug.ini
и дописать:
xdebug.max_nesting_level = 1000
В наших тестах, например, эта вешь не испольуется.
Далее в общем файле codeception.yml
раскомментируем опции анализатора покрытия (test coverage) нашего кода. Включаем только папки с кодом и исключаем всё лишнее. Другие секции нас вполне устраивают, поэтому всё остальное оставляем как есть:
actor: Tester paths: tests: tests log: tests/_output data: tests/_data helpers: tests/_support settings: bootstrap: _bootstrap.php memory_limit: 1024M colors: true modules: config: Yii2: cleanup: false coverage: enabled: true whitelist: include: - components/* - mail/* - modules/* - views/* exclude: - modules/admin/messages/* - modules/main/messages/* - modules/user/messages/*
На этом общее конфигурирование завершено.
Попробуем запустить всё снова, но с анализом покрытия:
vendor/bin/codecept run unit,functional --coverage-html
И после завершения посмотрим _output/coverage/index.html
и увидим примерно следующее:
Это удобно тем, что можно посмотреть, какие фрагменты кода не выполнялись и дописать проверки для них. Теперь нам не страшно будет вносить правки в исходный код. В любой момент можно проверить правильность работы проекта.
С тестами пока всё. Далее мы перекомпонуем наши модули для их большей самостоятельности.
Следующая часть: Организация переносимых модулей
Большущее спасибо! Вы, как всегда, на высоте!
Неделю, как взялся за чтение Вашего блога. Понял в yii2 больше, чем за месяц до этого. Очень качественная и подробная информация.
Жду не дождусь статьи по подключению rbac.
И еще, что бы работал coverage, в php.ini должен быть включен php_xdebug.dll, иначе будет ругаться на отсутствие драйвера - No code coverage driver available
Спасибо! Добавил в статью.
Спасибо за очередную интересную статью, Очень интересует, как можно сделать правильно авторизацию, через социальные сети. В рамках данного проекта.
Есть пример на эту тему в самом расширении.
Спасибо
Большое спасибо за вашу титаническую работу. Таких учебных материалов по тестированию php-проектов еще не было. Хочется попросить вас не оставлять эту тему и при развитии этого проекта обратить внимание на технологию тестирования javascript/ajax с помощью приемочных тестов, ну а также побольше внимания уделить mocks/stubs в модульном тестировании
Подскажите в чём беда. Я сделал всё по инструкции, но у меня ломаются тесты acceptance.
После долгих мучений, я скачал и установил себе голый yii2-app-advanced и с ним та же беда, не отрабатывают тесты для LoginPage например, а именно те, которые должны увидеть ошибку:
и в _output в html и скриншоте вижу, что ошибка не выводилась.
При этом просто в браузере всё отрабатывает.
Забыл сказать, это только в случае с WebDriver.
Если запускать с PhpBrowser, то всё ок.
Возможно не успевают появиться. Можно попробовать паузы в sleep(...) увеличить.
Привет, а как сделать, чтобы перед запуском теста база полностью очищалась от данных из предыдущих тестов? Само как-то должно произойти, или я руками должен очистку делать, например в setUp()?
setUp() и tearDown() срабатывают перед и после каждого теста, верно?
Я вручную вычищаю. Либо в setUp(), либо в конце теста. А вообще стараюсь делать так, чтобы тесты друг другу не мешали.
А что за файл генерируется при каждом тестировании?
Содерживое его не меняется, а только [STAMP]
В общем-то вот такое содержимое
Раньше в 2.0 для подключения модулей перегенерировался целиком весь AcceptanceTester. Это не позволяло добавлять туда свои методы вроде $I->login('name', 'password'), так как при перегенерации все наши методы перезатирались.
В 2.1 каждый тестер разбит на две части: сам класс и его автогенерируемый трейт. Теперь в класс можно помещать свои методы, не боясь их перезатирания:
Пример из http://codeception.com/docs/06-ReusingTestCode.
Как с ним правильно поступить? Заигнорить в гите?
Можно и заигнорить.
Дмитрий, такой вопрос по тестированию.
Исходные данные:
есть RBAC на основе DB manager. есть пользователи с разными ролями(у ролей свои разрешения и т.д.).
Вопросы:
как можно тестировать настройки доступа по RBAC, если пользователям нужно выставить права/роли при регистрации/добавлении?
т.е. к примеру хочу использовать фикстуры, но тогда как мне одновременно с определением пользователей задать привязки ролей (через $this->users[0] - можно ведь получить только данные фикстуры, но не ID вставленной записи - или ошибаюсь?)?
или если я задам в основной БД все роли и заранее создам тестовых пользователей, то смогу ли использовать фикстуры (не перепишется ли эта информация)? имею ввиду фикстуры как для таблицу пользователей, так и для других таблиц?
как я понимаю каждая фикстура отвечает за свою таблицу, верно?
заранее спасибо!
> если я задам в основной БД все роли и заранее создам тестовых пользователей, то смогу ли использовать фикстуры (не перепишется ли эта информация)
Именно этих тестовых пользователей с ролями и создавайте в фикстурах. Они для этого и предназначены. Тестирование производится в тестовой БД, а не в основной. Все данные из фикстур пойдут туда.
> как я понимаю каждая фикстура отвечает за свою таблицу, верно?
Да. И к тесту регистрации подключайте все нужные для него UserFixture, AuthItemFixture, AuthAssignmentFixture и т. п.
> можно ведь получить только данные фикстуры, но не ID вставленной записи - или ошибаюсь?
Для заранее создаваемых в фикстурах пропишите id:
и получайте через $this->users[0]['id'].
А вновь созданных после регистрации (которых нет в $this->users) можно просто найти в базе по имени, под которым их регистрировали:
и у них брать $user->id;
Все супер, труд титанический. Жаль, что не работает. Вроде всё по инструкции, ан нет.
Для меня проблема в том, что я не могу отличить обзорный код от рабочего. Сперва пишут так, далее вы говорите, что теперь нужно так, переделываю, потом еще, потом еще, потом ничего не работает и очень сложно понять что. Возможно, лучше отделить то, что в итоге не будет внесено в проект спойлером. Сейчас нереально проследить нить изменений.
С первой статьи проекта, рабочего кода нет, лишь концепт. В голове каша. У меня другие версии компонентов и php, может по этому.
Смотрел около семи часов скринкаст по тестированию, теперь применяю миграции для тестов и тупик полный, ничего за эти ошибки нет в сети.
Просто год назад я не знал, что и как буду переделывать сегодня.
Согласен, что теперь сделаешь... как есть. Я думал сперва, вы специально так сделали. Чтобы мы мозг включали. Но местами слишком запутано получается. Спасибо, что делитесь своим опытом разработки, это главное. Тяжело найти реальный опыт, к тому же, вы показываете множество вариантов подхода. Уже почти досмотрел, что у вас есть, буду ждать новых вебинаров и статей. Удачи.
Круто, спасибо за статью! Только начала по ней настраивать свое приложение и тут же вопрос возник. Дело в том, что в моем приложении есть 2 модуля (user, rbac), остальное пока в бекенде (categories, posts). Так вот миграции по модулю user лежат в модуле: modules/user/mogrations, остальные миграции по постам и категориям как обычно в console/migrations. При запуске php tests/codeception/bin/yii migrate конечно пытается создаться таблицы только post && category, хотя сначала должна быть таблица user (на нее ссылаются таблицы приложения), но о ней (о миграции создания таблицы user) тесты ничего не знают.
Дмитрий, как можно указать тестам очередность директорий миграций, с которых должна быть сборка?
Подключите любое расширение вроде multipath-migrations и пропишите ему все пути с миграциями.
О, спасибо, буду пробовать
Подскажите пожалуйста по UNIT тестированию.
Не могу настроить автозагрузку классов
Есть например модель User c с методом
для того чтобы протестировать этот метод надо руками инклюдить файл
как автоматически их подключать?
и как запустить консольное приложения для тестирования экшенов консольного контроллера?
И не могу обратиться к БД
получаю ошибку TaskTest: User exception
Что я делаю не так? :(
В стандартных приложениях автозагрузка работает автоматически, если наследовать тесты от yii\codeception\TestCase.
А консольные контроллеры не тестировал.
да в том то и дело что наследуюсь от \yii\codeception\TestCase
и пытаюсь прогнать тест
и получаю ошибку
MePHP Fatal error: Class 'app\models\User' not found in /var/www/test1/tests/unit/CronControllerTest.php
/var/www/test1/tests/_bootstrap.php содержит:
чтото уже запутался куда рыть
Спасибо :)
никогда не пользовался dirname() потому и лопухнулся :(
Пишу юнит тест...
правила проверки
так вот в error на lang_id = -2 в ошибки не падает...все типа хорошо, если ставлю там строку 'test' - то ошибка выдается, что д.б integer....почему не проверяет на отрицательность..где я туплю...
Добавьте 'exist' валидатор для 'lang_id', если это связь.
Ошибка, потому что integer вполне может быть отрицательным числом, с точки зрения типа данных - это правильно и ошибки в валидации нет.
Укажите параметр
там, где написан тип данных (аналогично полю 'name' ниже).
Пытаюсь провести миграцию тестовой базы
В консоли валятся ошибки:
На просторах нэта нарыл информацию, мол нужно вставить блок:
в config/console.php.
Вставлял - не работает.
Почему? Или вставляю не туда? Или вообще всё неправильно?
Компонент errorHandler с errorAction должен быть только в config/web.php. В консоли и в common он не нужен.
Переместил созданный config-local.php в envoronments/dev/tests/codeception/
После этого, выполнил
В итоге в site/config появилась поддиректоря tests/codeception/config-local.php с файлом.
????
Так должно быть для паблик проектов?
Но, самая главная проблема - это то, что не работает мигация в тестовую БД.
Уже всё 10 раз пересмотрел.
Вот, что я делаю не так?
Почему обработчик ругается на errorHandler ?
> Переместил созданный config-local.php в envoronments/dev/tests/codeception/
Зачем?
> Но, самая главная проблема - это то, что не работает мигация в тестовую БД.
Настройки компонента errorHandler должны быть только в config/web.php.
Пропустил 1 подкаталог.
Переместил config-local.php, созданный в site/tests/codeception/config в директорию environments/dev/config/tests/codeception.
Как указано Вами Дмитрий.
После этого выполнил init.
Настройки компонента errorHandler только в config/web.php. В блоке components.
Вот и непонимаю почему валятся эксепшены?
Что не так?
Вообщем проблема с хандлерами возникла из-за того, что (опять же по инструкции выше) дополнил файл bin/yii с путями как для тестовых конфигов:
А нужно было так:
И шторм ни разу не выругался....
При попытке установить Composer выдаёт:
Так понимаю главный composer.json что лежит в C:\Users\SomeUser\AppData\Roaming\Composer\ у меня кривой?
Где брать оригинал?
Или я неверно рассуждаю?
В ... как раз данные об ошибке. Если там про bower-asset/jquery, то забыли установить asset-plugin первой строчкой.
Сейчас нет возможности просмотреть. Может быть ошибка связана с тем, что у меня уже был установлен Codeception 2.2.1 глобально?
Вобщем так и есть, ошибки возникают из-за наличия установленной версии 2.1.*.
Удалил. Установил указанную.
Почему Шторм ругается на строку:
use Codeception\Module;
в FixtureHelper ?
Нэймспэйс Codeception найти не может. Да и я тоже.
В PhpStorm зайти в File - Settings - Languages & Frameworks - PHP.
Там в Include path добавить папку C:\Users\SomeUser\AppData\Roaming\Composer\vendor
Добрый вечер.
При попытке запустить:
выскакивает ошибка:
На всякий случай выполнил команду
tests\codeception\bin>codecept bootstrap
Не помогает.
В чём может быть дело?
Запускайте в папке tests, а не tests/codeception/bin.
Подскажите пожалуйста, что делать с FOREIGN_KEY_CHECKS. Глобально в субд конечно работает соблюдение ссылочной целостности. Но я не понимаю, делал по примеру, таблица вроде у нас одна только, user, и в ней нет внешних ключей.
Если запускать функциональные тесты с анализом покрытия кода
то после того как тесты прошли, появляется ошибка (без анализатора всё нормально)
и еще после того как проходят acceptance тесты, не могу посмотреть фэйлы и ошибки, тоже выскакивает
MySQL server has gone away - значит MySQL отключился по таймауту.
Добрый вечер.
Дмитрий, почему не могу обнаружить после завершения
файл _output/coverage/index.html
В _output даже папки coverage нет.
Где-то прописать нужно? Где?
А на свежем yii2-app-basic появляется?
Дмитрий, добрый день.
Прошу прощения за продолжение оборвавшегося диалога, небыло доступа к ПК.
Да, на свежем yii2-app-basic появляется.
Где я мог ошибиться?
А в codeception.yml секцию coverage настроили?
Да всё настроено.
Вообщем пропустил этот момент временно. Перешёл к другой главе. Некоторое время спустя. Попробовал протестить и всё появилось. Видимо с Гита скопировал часть кода, относящуюся к следующей главе.
P.S. давно Вас не было слышно...
Здравствуйте!
Подскажите, почему не проходит часть тестов. Запускаю так codecept run unit.
Вываливаются ошибки вида:
Результат
Уберите опцию --no-colors отовсюду.
Скажите, пожалуйста, подробнее где эта опция задается? В ваших статьях ее не нашел.
Если в PhpStorm по видео настраивали, то там. А если этого нет, то значит в другом дело. Видимо, что PHPUnit обновился сильно.
Нет, PhpStorm не настраивал по видео. Делал, все, как было в ваших постах SeoKeys.
Версии PHPUnit и Codeseption:
Codeception PHP Testing Framework v2.0.16
Powered by PHPUnit 4.7.7 by Sebastian Bergmann and contributors.
У меня, на этой машине стоит PHP 7, я думаю из-за этого возникает проблема. Пробовал, запускать тесты на проекте с гитхаба, были такие же проблемы.
Может быть. А стандартные со стабильного yii2-app-basic запускаются?
Да, со стандартными, которые идут в yii2 basic, все ок.
Time: 871 ms, Memory: 10.00MB
OK (4 tests, 15 assertions)
Запустил тесты, все ОК. А когда свой начал писать, застрял. Дело в том, что в форма появляется с помощью ajax, это я победил с помощью selenium и wait. Но еще в ней несколько селектов, при изменении первого происходит запрос на сервер и заполняется второй, ну и так по цепочке. Так вот в первом я выбрал вариант с помощью $I->selectOption('theme', 1); Потом дал время на подгрузку для второго:
Но все равно не могу из второго ничего выбрать. Я так понимаю, для selenium-а то, что мы в первом селекте что-то выбрали, на является поводом запускать событие change? Как мне его симулировать с помощью codeception, не подскажете? Я не нашел. Спасибо!
Сразу уточняю, executeJS вызывает ошибку
RuntimeException: Call to undefined method AcceptanceTester::executeJS
Обновил статью на новую структуру папки tests с Yii 2.0.10.
Спасибо!
Дмитрий, сообщество, добрый день!
Есть вопрос касательно модульного тестирования ActiveRecord, в котором имеется поле с проверкой на уникальность.
Имеется тестовый метод, который использует провайдер данных и таким образом проверяется отработка валидаторов, в том числе на уникальность. Способ в общем то очень похож на тот, который у Вас в этом видео. Но вот в чем проблема: в таблице до начала теста уже могут быть какие-то данные, что в некоторых случаях делает тест провальным. То есть нарушается повторяемость тестов.
Как можно побороть данную проблему? Можно ли как-то очищать таблицу перед выполнением тестового метода?
Изначально расчет был на фикстуры. Предполагалось, что после окончания теста фикстуры удаляются из таблицы БД, но это не так - таблица очищается непосредственно перед применением фикстур.
Поэтому вопрос: как очистить таблицу перед выполнением тестов, чтобы обеспечить повторяемость? Есть ли для этого в codeception какие либо стандартные средства?
Сделайте файл данных с пустым return []; и укажите его как dataFile в haveFixtures к этому тесту.
Спасибо! Как же это все таки было очевидно. И почему я не додумался. :)
Добрый день!
Ставил все по инструкции https://github.com/yiisoft/yii2-app-advanced/blob/master/docs/guide/start-testing.md,
Там же есть один из примеров - запуск тестов из папки common:
vendor/bin/codecept run -- -c common
А как запустить определенный класс/метод на тест, а не все тесты из папки?
Заранее большое спасибо!
Дмитрий, спасибо огромное!
А в функциональных тестах Cest можно использовать аннотацию @dataProvider?
Надо ли от чего-нибудь наследовать?
Если нет как можно запустить один тест с разными параметрами?
Пример:
вернее указать первым аргументом FunctionalTester $I для функциональных тестов. Сам пример, конечно, просто для ориентира)
Дмитрий, большое спасибо за труд, столько полезных материалов - это здорово!
Здравствуйте, может у кого тоже была такая проблема :
Добавил в папку Silenium и geckodriver, пишу команду:
Начинается запуск:
И на этом моменте запуск бесконечно зависает.
Как можно это поправить? Или хотя бы понять в чем проблема?
Попробовал использовать версию 3.0.1
выдало другой лог перед зависанием
Разобрался.
Весьма не очевидный для новичка момент:
Запускается строка
Дальше нужно открыть новое окно консоли и там уже запустить yii, и снова открыть новое окно и там уже запускать тесты.
Доброй ночи.
Пытаюсь настроить codeception так же как phpunit, в phpstorm. В настройках Languages & Frameworks -> PHP -> Test Frameworks
При запуске тестов получаю предупреждение
Как это можно исправить?
Здравствуйте. Как я могу найти документацию где будет все методы, которые используется в тестах. Один пример $I->submitForm().
На сайте Codeception.
Добрый день!
Помогите устранить ошибку, не могу запустить тесты
Спасибо!
Обновите вендоры. Может быть поможет.
Это не помогает
Значит что-то уже сильно устарело.
Используйте php7.2+
Нигде не могу найти информацию - как залить дамп базы перед запуском тестов, чтобы при этом использовать данные из общих настроек. То есть - у codeception есть модуль Db, который умеет заливать дамп. Но он требует, чтобы подключение к базе данных ему указывали отдельно прям для него, в файле codeception.yml - писали там dns, username, password. И я никак не могу найти информацию, как ему скормить те данные, которые уже есть в системе. Чтобы не указывать их второй раз в этом файле. Потому что у меня должно всё запускаться в CI/CD, и на проекте сидит несколько разработчиков, у каждого свои настройки. А тут получается, что надо жёстко задать одни настройки для всех.
Может быть Вы подскажите, как мне залить нужные данные в базу?
Если проект на Yii2, то можно использовать его фикстуры и всё будет работать сразу.
Если же просто модуль Db, то можно подставлять переменные окружения из вспомогательного файла params.php или из .env локально и из секретов в CI пайплайне.
А вообще можно внедрить Docker, чтобы у всех разработчиков были одинаковые фиксированные настройки.
Дмитрий, подскажите, пожалуйста, как в файле ./vendor/bin/codecept предустановленная константа имеет значение ./vendor/codeception/codeception, ведь она должна указывать на вышеуказанную папку с bin
Про какую константу идёт речь?
Про константу __DIR__
Но я уже разобрался. В папке ./vendor/bin/ лежат символические ссылки и в константу __DIR__ падает не путь ./vendor/bin/, а тот, на который указывает символическая ссылка.
Спасибо