Начните свое PHP-тестирование с Codeception
Опубликовано: 2022-03-11Прежде чем перейти к Codeception и PHP, мы должны рассмотреть основы и начать с объяснения того, зачем нам в первую очередь нужно тестирование в приложениях. Возможно, мы могли бы завершить проект, не тратя время на тесты, по крайней мере, в этот раз?
Конечно, вам не нужны тесты для всего; например, когда вы хотите создать еще одну домашнюю страницу. Вам, вероятно, не нужны тесты, когда ваш проект содержит статические страницы, связанные одним маршрутизатором.
Тем не менее, вам определенно нужно тестирование, когда:
- Ваша команда использует BDD/TDD.
- Ваш репозиторий Git содержит более пары коммитов.
- Вы настоящий профессионал, работаете над серьезным проектом.
Вы можете извиниться, сказав, что у вас уже есть специальный отдел тестирования, группа людей, которые проводят тесты и пишут новые, когда это необходимо. Но представляете, сколько времени займет исправление ошибок после того, как вы добавите новый функционал в свой проект?
Что решает тестирование?
Во-первых, давайте решим, какие проблемы могут быть решены с помощью тестирования. Вы не можете избавиться от всех своих ошибок с помощью тестирования, но вы можете описать ожидаемое поведение в тестовых примерах. Ошибки могут быть внутри ваших тестовых случаев. Спагетти-код остается спагетти-кодом даже при использовании тестовых случаев.
Однако вы можете быть уверены, что впоследствии ваш код будет изменен (путем исправления ошибок или добавления новых функций), поэтому ваш код по-прежнему будет свободен от ошибок, описанных в тесте. Кроме того, даже хорошо написанные тесты иногда могут использоваться в документации, потому что там можно увидеть, как разворачиваются типовые сценарии, и проверить ожидаемое поведение. Можно сказать, что тестирование — это небольшая, но важная инвестиция в будущее.
Итак, какие тесты мы можем использовать?
- Модульные тесты: Низкоуровневые тесты, которые проверяют небольшие фрагменты вашего кода — методы вашего класса, изолированные от другого кода.
- Интеграционное тестирование. Интеграционные тесты проверяют часть вашего приложения, они могут содержать несколько классов или методов, но должны быть ограничены одной функцией. Этот тест также должен проверять, как взаимодействуют разные классы.
- Функциональное тестирование: тестирует конкретные запросы к вашему приложению: ответ браузера, изменения в базе данных и так далее.
- Приемочное тестирование. В большинстве случаев приемочное тестирование означает проверку соответствия приложения всем требованиям клиента.
Чтобы пояснить, предположим, что мы проиллюстрируем процесс чем-то материальным, например, зданием. Здание состоит из небольших блоков, образующих стены. Каждый кирпич должен соответствовать определенным требованиям; он должен выдерживать необходимую нагрузку, иметь определенный объем и форму и так далее. Это модульные тесты. Идея интеграционных испытаний состоит в том, чтобы проверить, насколько плотно и точно кирпичи прилегают друг к другу, насколько они интегрированы в тот или иной элемент здания. Функциональные испытания можно сравнить с испытаниями на одной стене здания, чтобы проверить, защищен ли интерьер от непогоды, и можно ли увидеть солнце через окно. Приемочные испытания включают в себя тестирование всего здания как законченного продукта: откройте дверь, войдите внутрь, закройте дверь, включите свет, поднимитесь на второй этаж и посмотрите на сад снаружи здания.
Познакомьтесь с кодецепцией
Однако это деление условно и иногда трудно удержаться от соблазна смешения разных видов тестов.
Многие разработчики используют модульные тесты и утверждают, что этого достаточно. Я был одним из таких разработчиков; Я обнаружил, что использование разных систем для разных типов тестов слишком сложно и требует много времени. Некоторое время назад я решил найти что-то более полезное, чем PHPUnit; Я хотел лучше тестировать свой код, но не хотел читать и изучать тонны документации и искать подводные камни. Так я открыл для себя Codeception. Сначала я был настроен скептически, как мы часто относимся к чему-то новому (этому проекту пять лет, так что технически его нельзя считать «новым»), но поигравшись с ним пару дней , я пришел к выводу, что Codeception — очень полезная и мощная система.
Так как же установить Codeception? Это так же просто, как и получается:
$ composer require "codeception/codeception" $ php vendor/bin/codecept bootstrap
После установки вы обнаружите в своем проекте новую папку с именемtests , а также несколько подпапок с именами Acceptance, Functional и Unit . Похоже, мы можем начать писать наши тесты. Круто, но что дальше?
Теперь попробуйте добавить стандартный приемочный тест Hello World.
$ php vendor/bin/codecept generate:cept acceptance HelloWorld
Теперь мы получаем файл приемочного тестаtests/acceptance/HelloWorldCept.php со следующим содержимым:
<?php $I = new AcceptanceTester($scenario); $I->wantTo('perform actions and see result');
Переменная по умолчанию с именем $I
— это не просто буква; это персонаж. Что проводит испытания? Тестер, очевидно же. Этот тестер открывает страницу или класс вашего веб-сайта, что-то с ним делает и показывает конечный результат своих действий. Вы увидите, что сработало, а что пошло не так. Вот почему этот объект называется $I
и содержит методы wantTo()
, see()
или amOnPage()
.
Итак, давайте подумаем, как тестер, о способах проверки работоспособности страницы. Первый подход заключается в открытии страницы и поиске фразы. Это доказывает, что страница доступна для посетителей.
Это должно быть легко:
<?php $I->amOnPage('/'); $I->see('Welcome');
Мы можем использовать эту команду для запуска тестов Codeception:
$ php vendor/bin/codecept run
Мы сразу видим, что что-то не так. На первый взгляд кажется, что сообщение слишком длинное и непонятное, но если присмотреться, то все становится очевидным.
У нас был один тест Acceptance , и он обнаружил ошибку:
Acceptance Tests (1) Perform actions and see result (HelloWorldCept) Error ---------- 1) Failed to perform actions and see result in HelloWorldCept (tests/acceptance/HelloWorldCept .php) [GuzzleHttp\Exception\ConnectException] cURL error 6: Could not resolve host: localhost (see http://curl.haxx.se/libcurl/c/libcurl-errors.html)
Это виновник: localhost недоступен.
А вот этапы сценария нашего теста:
1. $I->amOnPage("/")
Хорошо, давайте откроем тесты/acceptance.suite.yml и изменим url: http://localhost/
на то, что действительно доступно. В моем случае это мой локальный тестовый хост, url: https://local.codeception-article.com/
Запустите тест еще раз, и вот что у вас должно получиться:
Acceptance Tests (1) --------------------------------------------------------------------------------------- Perform actions and result (HelloWorldCept) Ok
Ура! Наш первый успешный тест!
Конечно, amOnPage()
— не единственный доступный метод тестирования, мы просто выделили его для нашего примера. Все методы тестирования Codeception можно разделить на следующие группы:
- Взаимодействие со страницей:
fillField()
,selectOption()
,submitForm()
,click()
- Утверждения.
see()
,dontSee()
,seeElement()
,seeInCurrentUrl()
,seeCheckboxIsChecked()
,seeInField()
,seeLink()
. Ко всем этим методам вы можете добавить суффикс и использовать его, когда вам нужен метод, который не будет прерывать тестовый сценарий, когда что-то не может быть найдено. - Методы куки:
setCookie()
,grabCookie()
,seeCookie()
- Комментарий и описание тестовых сценариев:
amGoingTo()
,wantTo()
,expect()
. Используйте эти методы, чтобы получить хорошо прокомментированные и описанные тесты, которые помогут вам запомнить цели теста.
Итак, если бы нам нужно было протестировать страницу электронной почты для сброса пароля, мы могли бы сделать это следующим образом:
<?php $I = new AcceptanceTester($scenario); $I->wantTo('Test forgotten password functionality'); $I->amOnPage('/forgotten') $I->see('Enter email'); $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->expect('Reset password link not sent for incorrect email'); $I->see('Email is incorrect, try again'); $I->amGoingTo('Fill correct email and get link'); $I->see('Enter email'); $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->expect('Reset password link sent for correct email'); $I->see('Please check your email for next instructions');
Похоже, это должно сработать, но что, если на странице есть части, загруженные с помощью Ajax? Можем ли мы протестировать такую страницу? Ответ заключается в том, что Codeception по умолчанию использует PhpBrowser, основанный на Symfony BrowserKit и Guzzle. Это просто, быстро, и вам нужен только curl, чтобы использовать его.

Вы также можете использовать Selenium и тестировать страницы в реальных браузерах. Да, это будет медленнее, но вы также сможете протестировать JavaScript.
Во-первых, вам нужно установить драйвер Selenium, изменить accept.suite.yml и пересобрать класс AcceptanceTester. После этого вы можете использовать методы wait()
и waitForElement()
. И, что более интересно, вы сможете сэкономить свое время и ресурсы, используя методы saveSessionSnapshot()
и loadSessionSnapshot()
. Этот метод позволяет сохранять состояние сеанса и запускать новые тесты в более ранних сеансах. Это полезно в некоторых ситуациях, например, в процессе авторизации теста.
Таким образом, мы получаем простую, но мощную возможность тестировать множество функций.
Функциональное тестирование
Ладно, пора переходить к функциональному тестированию.
$ php vendor/bin/codecept generate:cept functional HelloWorld
И вот что мы получаем:
<?php $I = new FunctionalTester($scenario); $I->amOnPage('/'); $I->see('Welcome');
Чего ждать?
Нет, это не ошибка. Функциональные тесты должны быть написаны так же, как интеграционные тесты. Разница в том, что функциональные тесты напрямую взаимодействуют с вашим приложением. Это означает, что вам не нужен веб-сервер для запуска функционального тестирования, и у вас есть больше возможностей для тестирования различных частей вашего приложения.
Это означает, что поддержка всех фреймворков отсутствует, но список поддерживаемых фреймворков обширен: Symfony, Silex, Phalcon, Yii, Zend Framework, Lumen, Laravel. Этого должно быть достаточно для большинства случаев и большинства разработчиков. Пожалуйста, обратитесь к документации модуля Codeception, чтобы получить список доступных функций, а затем просто включите его в functional.suite.yml
.
Прежде чем мы перейдем к модульному тестированию, позвольте мне сделать небольшое отступление. Как вы, возможно, заметили, мы создали наши тесты с ключевым словом:
$ php vendor/bin/codecept generate: cept acceptance HelloWorld
Это не единственный способ создания тестов. Есть также цест-тесты . Разница в том, что вы можете структурировать несколько связанных сценариев в одном классе:
$ php vendor/bin/codecept generate:cest acceptance HelloWorld <?php class HelloWorldCest { public function _before(AcceptanceTester $I) { $I->amOnPage('/forgotten') } public function _after(AcceptanceTester $I) { } // tests public function testEmailField(AcceptanceTester $I) { $I->see('Enter email'); } public function testIncorrectEmail(AcceptanceTester $I) { $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->see('Email is incorrect, try again'); } public function testCorrectEmail(AcceptanceTester $I) { $I->fillField('email', '[email protected]'); $I->click('Continue'); $I->see('Please check your email for next instructions'); } }
В этом примере методы _before()
и _after()
выполняются до и после каждого теста. В каждый тест передается экземпляр класса AcceptanceTester
, поэтому вы можете использовать его так же, как и в тестах cest. Этот стиль тестов может быть полезен в определенных ситуациях, поэтому его стоит иметь в виду.
Модульное тестирование
Пришло время для модульного тестирования.
Codeception основан на PHPUnit, поэтому вы можете использовать тесты, написанные для PHPUnit. Чтобы добавить новые тесты PHPUnit, используйте следующий подход:
$ php vendor/bin/codecept generate:phpunit unit HelloWorld
Или просто наследуйте свои тесты на \PHPUnit_Framework_TestCase
.
Но если вы хотите чего-то большего, попробуйте модульные тесты Codeception:
$ php vendor/bin/codecept generate:test unit HelloWorld <?php class HelloWorldTest extends \Codeception\TestCase\Test { /** * @var \UnitTester */ protected $tester; protected function _before() { } protected function _after() { } // tests public function testUserSave() { $user = User::find(1); $user->setEmail('[email protected]'); $user->save(); $user = User::find(1); $this->assertEquals('[email protected]', $user->getEmail()); } }
Пока ничего необычного. Методы _before()
и _after()
являются setUp()
и tearDown()
и будут выполняться до и после каждого теста.
Основное преимущество этого теста заключается в его способности расширить процесс тестирования за счет включения модулей, которые можно включить в unit.suite.yml
:
- Доступ к memcache и базам данных для отслеживания изменений (поддерживаются MySQL, SQLite, PostgreSQL, MongoDB)
- Тестирование приложений REST/SOAP
- Очереди
Каждый модуль имеет свои особенности, поэтому лучше проверить документацию и собрать необходимую информацию по каждому модулю, прежде чем переходить к реальным тестам.
Кроме того, вы можете использовать пакет Codeception/Specify (который нужно добавить в composer.json
) и написать такое описание:
<?php class HelloWorldTest extends \Codeception\TestCase\Test { use \Codeception\Specify; private $user; protected function _before() { $this->user = User::find(1); } public function testUserEmailSave() { $this->specify("email can be stored", function() { $this->user->setEmail('[email protected]'); $this->user->save(); $user = User::find(1); $this->assertEquals('[email protected]', $user->getEmail()); }); } }
PHP-код внутри этих замыкающих функций изолирован, поэтому изменения внутри не повлияют на остальную часть вашего кода. Описания помогут вам сделать тест более читабельным и упростить выявление неудачных тестов.
В качестве дополнительной опции вы можете использовать пакет Codeception\Verify
для синтаксиса, подобного BDD:
<?php public function testUserEmailSave() { verify($map->getEmail())->equals('[email protected]'); }
И, конечно же, вы можете использовать заглушки:
<?php public function testUserEmailSave() { $user = Stub::make('User', ['getEmail' => '[email protected]']); $this->assertEquals('[email protected]', $user->getEmail()); }
Вердикт: Codeception экономит время и силы
Так чего же ожидать от Codeception? Для кого это? Есть ли предостережения?
На мой взгляд, эта среда тестирования подходит для самых разных команд: больших и малых, начинающих и закаленных в боях профессионалов PHP, тех, кто использует популярный фреймворк, и тех, кто не использует какой-либо фреймворк.
В любом случае все сводится к следующему: Codeception готов к работе в прайм-тайм.
Это зрелая и хорошо документированная структура, которая может быть легко расширена многочисленными модулями. Codeception современен, но основан на проверенном временем PHPUnit, что должно успокоить разработчиков, которые не хотят слишком много экспериментировать.
Он работает хорошо, что означает, что он быстрый и не требует слишком много времени и усилий. Более того, его относительно легко освоить, а обширная документация должна способствовать беспроблемному процессу обучения.
Codeception также легко установить и настроить, но при этом он может похвастаться множеством дополнительных опций. Хотя большинству пользователей не нужны все (или даже большинство) из них, все зависит от того, что вы собираетесь с ними делать. Вы можете начать с основ, а дополнительные функции рано или поздно пригодятся.