
В Laravel-5, из коробки, есть поддержка фреймворка для тестирования PHPUnit. Кроме того, Laravel предоставляет набор помощников для тестирования самых распространенных ситуаций в функциональных тестах. Но эти помощники специфичны и их нужно освоить перед использованием. Поэтому, если вы знакомы с Codeception, то лучше использовать этот универсальный фреймворк, т.к. он позволяет дополнительно выполнять приемочное тестирование и содержит функционал облегчающий процесс тестирования в целом. А для его лучшей интеграции с фреймворком Laravel, создан специальный модуль Laravel5.
Установка Codeception.
Папку tests, которая находится в корне проекта, удаляем. Все равно там находится только пара демонстрационных тестов, в отличии от того же Yii2.В файле composer.json в блоке "require-dev" прописаны зависимости:
"mockery/mockery": "0.9.*", "phpunit/phpunit": "~6.0"они, при использовании Codeception, не понадобятся. Удаляем эти строки и обновляем зависимости проекта:
composer update
Если Codeception у вас еще не установлен, то устанавливаем (глобально или в данный Laravel-проект).
Глобально:
composer global require codeception/codeception --devв текущий проект:
composer require codeception/codeception --devПодробнее про установку в этой статье.
Далее создаем тестовую базу данных, например с помощью PhpMyAdmin. Название ее можно взять на основе названия основной БД плюс нижнее подчеркивание test. Например:
project_test
Копируем файл .env и сохраняем его так же в корне проекта с названием .env.testing
Указываем в нем данные для соединения с тестовой базой данных. Это по-меньшей мере строка с ее названием.
Выполнить команду инициализации, которая создаст в корне проекта главный конфигурационный файл codeception.yml и каталог tests с различными папками и файлами:
codecept bootstrap
Основной конфигурационный файл пока можно оставить без изменений и настроить только конфигурацию каждого отдельного типа тестов.
Конфигурация приемочных тестов.
В комментариях к модулю Laravel5 указано, что он позволяет запускать функциональные тесты для Laravel 5.1+ и не должен использоваться для приемочных тестов. Так же, написано, что если вы хотите использовать Eloquent в своих приемочных тестах (в паре с WebDriver), нужно включить только ORM часть этого модуля (файл tests/acceptance.suite.yml):modules: enabled: - WebDriver: browser: chrome url: http://127.0.0.1:8000 - Laravel5: part: ORM environment_file: .env.testing
Использование ORM части модуля Laravel5 значит возможность использования методов, которые взаимодействуют с базой данных.
Касательно всех возможных настроек модуля будет написано ниже.
Доступные методы ORM данного модуля:
* have
* haveMultiple
* haveRecord
* grabRecord
* seeRecord
* dontSeeRecord
Посмотреть эти и остальные методы модуля Laravel5 с кратким описанием и примерами использования можно в файле модуля vendor\codeception\codeception\src\Codeception\Module\Laravel5.php
Таким образом, например, после заполнения формы сохранения данных текущего пользователя приемочным тестом, вы можете используя метод seeRecord(), модуля Laravel5, проверить наличие сохраняемых данных в БД:
$I->seeRecord('App\User', array('name' => 'Ivan', 'email' => 'ivan@gmail.com', 'password' => '123456'));
Т.е. для приемочных тестов в любом случае должны быть подключены модули PhpBrowser или WebDriver. Об этом я писал подробно тут.
Конфигурация функциональных тестов.
Пример файла конфигурации функциональных тестов tests/functional.suite.yml:actor: FunctionalTester modules: enabled: - \Helper\Functional - Laravel5: environment_file: .env.testing cleanup: true
Конфигурация модульные тестов.
Пример файла конфигурации модульных тестов tests/unit.suite.yml:actor: UnitTester modules: enabled: - Asserts - \Helper\Unit - Laravel5: environment_file: .env.testing cleanup: true
Возможные настройки модуля Laravel5:
- cleanup: `boolean`, по-умолчанию `true` - все запросы базы данных будут выполняться в транзакции,будет откат в конце каждого теста.
- run_database_migrations: `boolean`, по-умолчанию `false` - выполнять миграцию базы данных перед каждым тестом.
- database_migrations_path: `string`, по-умолчанию `null` - путь к миграции базы данных по отношению к корню приложения.
- run_database_seeder: `boolean`, по-умолчанию `false` - запустить загрузку тестовыми данными базы данных перед каждым тестом.
- database_seeder_class: `string`, по-умолчанию ` `- имя класса сеялки базы данных.
- environment_file: `string`, по-умолчанию `.env` - файл среды для загрузки для тестов.
- bootstrap: `string`, по-умолчанию `bootstrap/app.php` - относительный путь к конфигурационному файлу app.php.
- root: `string`, по-умолчанию ` `- корневой путь приложения.
- packages: `string`, по-умолчанию `workbench` - корневой путь пакетов приложений (если есть).
- vendor_dir: `string`, по-умолчанию `vendor` - необязательный относительный путь к каталогу поставщика.
- disable_exception_handling: `boolean`, по-умолчанию `true` - отключить обработку исключений Laravel.
- disable_middleware: `boolean`, по-умолчанию `false` - отключить все промежуточное ПО.
- disable_events: `boolean`, по-умолчанию `false` - отключить события (не отключает модельные события).
- disable_model_events: `boolean`, по-умолчанию `false` - отключить модельные события.
- url: `string`, по-умолчанию ` `- URL приложения.
Самые нужные из них:
run_database_migrations: trueустановив данную опцию в true, модуль проверит выполнены ли все миграции, если нет – выполнит их относительно тестируемой базы данных.
cleanup: falseустановив данную опцию в false, вы отмените выполнение запросов в транзакции и тестовая база будет заполняться данным при выполнении тестирования.
Согласно существующих опций модуля Laravel5 видно, что можно создать и подключить отдельную папку для миграций которые должны выполниться в тестовой базе данных. А так же подключить отдельный класс для заполнения ее тестовыми данными.
Подробнее про настройку конфигурации данного модуля в файле vendor\codeception\codeception\src\Codeception\Module\Laravel5.php.
После внесения правок в конфигурационные файлы нужно выполнить команду:
codecept build
Примеры тестов.
Для проверки правильности подключения Codeception и демонстрации использования модуля Laravel5 создадим пару тестов.Простой функциональный тест.
Т.к., возможно, у вас установлен «чистый» фреймворк, особо тестировать нечего. Используем метод модуля Laravel5 amOnRoute() для перехода на главную страницу, которая открывается сразу после установки фреймворка.Данный метод осуществляет переход на указанный именованный маршрут. Но после установки фреймворка Laravel прописан всего один простой маршрут без имени, поэтому открываем файл routes/web.php и дописываем имя маршруту:
Route::get('/', function () { return view('welcome'); })->name('home');
Создадим файл функционального теста для главной страницы:
codecept generate:cest functional Home
Добавим такой код в созданный файл tests/functional/HomeCest.php:
<?php class HomeCest { public function _before(AcceptanceTester $I) { } public function _after(AcceptanceTester $I) { } // tests public function tryToTest(AcceptanceTester $I) { $I->wantTo('check home page'); $I->amOnRoute('home'); $I->see('Laravel'); } }
Запускаем выполнение функциональных тестов:
codecept run functionalРезультат в случае успешного прохождения теста:

Модульный/интеграционный тест.
Интеграционное тестирование проводится с помощью тех же инструментов что и модульное (unit). Codeception не выделяет отдельно интеграционные тесты.Напишем тест, который проверяет метод модели User занимающийся удалением пользователя из базы данных по его id. Я выбрал модель User т.к. она уже существует сразу после установки фреймворка Larevel. Но методов в ней нет, поэтому для теста напишем один простой метод:
public static function deleteUser($id){ if(self::destroy($id)){ return ['status'=>'Пользователь удален']; } else { return ['error'=>'Ошибка удаления']; } }
Тест является интеграционным, поскольку тестирование выходит за границы одного этого метода и мы будем взаимодействовать с тестовой базой данных. Пример хорош тем, что я смогу показать как использовать модуль Laravel5 для взаимодействия с БД.
До выполнения тестов у вас уже должна быть создана и прописана в файл .env.testing тестовая БД, о чем я писал выше. Она может пока быть совсем чистая.
Настроим конфигурацию модуля Laravel5, файл tests/unit.suite.yml:
actor: UnitTester modules: enabled: - Asserts - \Helper\Unit - Laravel5: environment_file: .env.testing # откат всех запросов к БД после выполнения тестов (транзакции) cleanup: true # выполнить миграции перед каждым тестом run_database_migrations: true # использовать заполнение тестовыми данными run_database_seeder: true # указываем класс с тестовыми данными database_seeder_class: 'UserTestSeeder'
В комментариях я указал что зачем подключаю. Засорять базу данными в нашем случае незачем, поэтому я поставил cleanup: true. Включил выполнение миграций перед тестами. Если она у вас чистая, то автоматически выполнятся 2 стандартные миграции и у вас появится таблица users, которая нам нужна для тестирования. Я мог бы так же создать и указать папку со своими миграциями, нужными только для тестирования. Вы можете выполнить миграцию по созданию таблицы users вручную и этот пункт вообще удалить. Т.к. в тесте будет проверка данных которые должны находиться в таблице, я создам класс «сеялку» для автозаполнения таблицы перед каждым тестом. Ведь сама тестовая база будет без всяких данных в таблице users.
Создаем файл и класс сеялки консольной командой:
php artisan make:seeder UserTestSeeder
Заполняем его:
<?php use Illuminate\Database\Seeder; class UserTestSeeder extends Seeder { public function run() { \App\User::create([ 'id'=>'1', 'name'=>'TestName', 'email'=>'testname@mail.ru', 'password'=>'123456', ]); } }
Если создавать класс вручную, то при обращении к нему будет ошибка:
[ReflectionException]
Class ххх does not exist
потому, что папка с классами Seeder прописана в файле composer.json в поле classmap:
"autoload": { "classmap": [ "database/seeds" ],а значит, классы находящиеся в папке database/seeds не подключаются автоматически (поиска их в папке не будет), а прописываются в автозагрузчик Composer при выполнении консольной команды make:seeder. Но можно обновить автозагрузчик вызовом:
composer dump-autoload --optimize
Создаем тест:
codecept generate:cest unit User
Код тестов метода deleteUser() модели User будет таким, файл tests/unit/UserCest.php:
<?php use App\User; class UserCest { public function _before(UnitTester $I) { } public function _after(UnitTester $I) { } public function deleteUserTrue(UnitTester $I) { $id = 1; $I->seeRecord('users', array('id' => $id)); $result = User::deleteUser($id); $I->assertArrayHasKey('status', $result); $I->cantSeeRecord('users', array('id' => $id)); } public function deleteUserFalse(UnitTester $I) { $id = 777; $I->cantSeeRecord('users', array('id' => $id)); $result = User::deleteUser($id); $I->assertArrayHasKey('error', $result); } }
Т.к. метод работает с конкретным id пользователя которого нужно удалить из БД, в классе UserTestSeeder я указал конкретный id (1) для добавления тестового пользователя. Тестирующий метод deleteUserTrue() должен проверить наличие пользователя с id=1 в БД, поэтому я указываю id, который там будет после заполнения таблицы тестовыми данными.
Методы seeRecord и cantSeeRecord как раз предоставляет модуль Laravel5 и они позволяют проверить в таблице (название указывается первым аргуметом) наличие указанных параметров (передаются во втором параметре в массиве).
Т.е. строкой
$I->seeRecord('users', array('id' => $id));я проверяю что пользователь с таким id есть в таблице users до его удаления. Далее вызываю сам проверяемый метод чтобы он выполнился и пользователь был удален:
$result = User::deleteUser($id);
После удаления использую метод Codeception, а точнее метод PHPUnit
$I->assertArrayHasKey('status', $result);чтобы проверить что же вернул вызов метода. А возвращает он массив ['status'=>'Пользователь удален']. Поэтому я проверяю есть ли ключ 'status' в возвращаемом методом массиве.
И, чтоб наверняка, проверяю что пользователя с id=1 уже в таблице нет:
$I->cantSeeRecord('users', array('id' => $id));
Второй тестирующий метод deleteUserFalse() строится по-аналогии. Только я использую id которого точно нет в таблице users (777), чтобы проверить вернет ли метод массив с ключем 'error' как было задумано.
Другие полезные методы модуля Laravel5.
Кроме ORM методов есть еще несколько интересных которыми можно пользоваться:$I->amOnRoute('posts.create');открывает веб-страницу с использованием имени и параметров маршрута.
$I->seeCurrentRouteIs('posts.index');проверяет, соответствует ли текущий URL-адрес маршруту.
$I->amOnAction('PostsController@index');открывает веб-страницу по имени действия.
$I->seeInSession('key', 'value');проверяет, соответствует ли переменная сеанса указанной.
$I->amLoggedAs(['username' => 'jane@example.com', 'password' => 'password']);устанавливает текущего пользователя.
$I->logout()выход пользователя.
А ознакомиться со всеми методами модуля с кратким описанием и примерами можно в его файле vendor\codeception\codeception\src\Codeception\Module\Laravel5.php
Кроме того, в конфигурационных файлах мы для каждого типа тестов подключаем дополнительные модули и помошники (например \Helper\Functional), соответственно можем использовать и их методы.