Перейти к основному контенту

Функциональное тестирование

Теперь, когда мы имеем написанные приемочные тесты, настало время рассмотреть функциональные тесты. Функциональные тесты — это почти то же самое, что и приемочные, однако есть одно существенное различие: они не требуют использования веб сервера для запуска своих сценариев. Другими словами, мы будем запускать наше приложение внутри тестов, имитируя запросы и ответы.

Говоря простыми словами, мы устанавливаем переменные $_REQUEST, $_GET и $_POST, затем выполняем скрипт внутри теста, получаем ответ и тестируем все это. Функциональное тестирование может быть лучшим решением, чем приемочное, потому как такие тесты не требуют использования веб-сервера и могут предложить более подробный отладочный вывод. К примеру, если ваш сайт выбросит исключение, оно будет напечатано в консоли.

Codeception может подключаться к различным веб фреймворкам, поддерживающим функциональное тестирование. К примеру, вы можете запустить функциональные тесты для приложения построенного поверх Zend Framework, Symfony или Symfony2, используя лишь модули поставляемые Codeception! Список поддерживаемых модулей будет разобран позже.

Модули для всех этих фреймворков имеют одинаковый интерфейс, поэтому ваши тесты не будут связаны друг с другом. Вот простой пример функционального теста:

<?php
$I = new TestGuy($scenario);
$I->amOnPage('/');
$I->click('Login');
$I->fillField('Username','Miles');
$I->fillField('Password','Davis');
$I->click('Enter');
$I->see('Hello, Miles', 'h1');
// $I->seeEmailIsSent() - special for Symfony2
?>

Это такой же тест, как и приемочный. Как видите, можно использовать одинаковые методы и для приемочных и для функциональных тестов. Мы рекомендуем тестировать нестабильные части приложения с помощью функциональных тестов, а стабильные - с помощью приемочных.

Подводные камни

Приемочные тесты обычно намного медленнее, чем функциональные. Однако, функциональные тесты менее стабильны и запускают тестовый фреймворк и приложение в одном окружении.

Headers, Cookies, Sessions

Одна из известных проблем функциональных тестов — использование PHP функций, работающих с переменными из категории headers, sessions, cookies. Как Вы знаете, функция header() возвратит ошибку, если будет выполнена более одного раза. В функциональных тестах мы запускаем наше приложение несколько раз, таким образом мы получим много ненужных ошибок при отображении результатов.

Разделяемая память

При функциональном тестировании, в отличие от традиционного, приложение PHP не останавливается после выполнения запроса. Так как все запросы выполняются в одном контейнере памяти, они не изолированны. Таким образом если вы заметили, что ваши тесты магическим образом падают, однако не должны — попробуйте выполнить один тест. Это проверит, изолированы ли тесты во время работы. Потому что довольно просто поломать окружение, когда все тесты выполняются в разделяемой памяти. Держите память в чистоте, избегайте утечек памяти и очищайте глобальные и статические переменные.

Основы функционального тестирования

Ваши функциональные тесты располагаются в каталоге tests/functional. Для начала Вам необходимо включить один из модулей фреймворков в конфигурационный файл тестового набора: tests/functional.suite.yml. Примеры конфигурации фреймворков описаны ниже в данной главе.

После вам необходимо пересобрать Guy-классы:

php codecept.phar build

Для генерации теста Вы можете использовать стандартную команду-генератор generate:cept:

php codecept.phar generate:cept functional myFirstFunctional

После чего выполнить тесты с помощью run:

php codecept.phar run functional

Используйте опцию --debug для более детального вывода.

Сообщения об ошибках

По умолчанию Codeception использует значение E_ALL & ~E_STRICT & ~E_DEPRECATED. В функциональных тестах Вы можете сменить эти значения в зависимости от используемого фреймворка. Сообщения об ошибках могут быть настроены в конфигурационном файле набора:

class_name: TestGuy  
modules:  
    enabled: [Yii1, TestHelper]  
error_level: "E_ALL & ~E_STRICT & ~E_DEPRECATED"  

error_level может быть установлен глобально в файле codeception.yml.

Фреймворки

Codeception интегрирован с большинством популярных PHP фреймворков. Разработчики нацелены на то, чтобы иметь модули для большинства популярных библиотек.

Помогите им в разработке, если Вы используете фреймворк, которого нет в списке.

Symfony2

Для интеграции Symfony2 Вам не нужно устанавливать никаких бандлов или менять конфигурацию. Просто подключите модуль Symfony2 в Ваш тестовый набор. Если вы используете Doctrine2, не забудьте подключить его тоже.

Пример для functional.suite.yml:

class_name: TestGuy
modules:
    enabled: [Symfony2, Doctrine2, TestHelper] 

По умолчанию модуль будет искать ядро в директории app.

Модуль использует Symfony Profiler для предоставления дополнительной информации.

Смотрите полную справку здесь

Laravel 4

Модуля для Laravel не имеет конфигурации и так же может быть легко настроен.

class_name: TestGuy
modules:
    enabled: [Laravel4, TestHelper]

Yii

Сам по себе Yii framework не имеет движка для функционального тестирования. Таким образом, Codeception первый и единственный фреймворк для функционального тестирования на Yii. Для использования его с Yii, включите модуль Yii1 в файл конфигурации.

class_name: TestGuy
modules:
    enabled: [Yii1, TestHelper]

Для того, чтобы избежать подводных камней, описаных ранее, Codeception предоставляет некоторые хуки поверх движка Yii. Для его установки следуйте шагам описаным в руководстве.

Zend Framework 2

Используйте модуль ZF2 для запуска функциональных тестов внутри Zend Framework 2.

class_name: TestGuy
modules:
    enabled: [ZF2, TestHelper]

Zend Framework 1.x

Модуль для Zend Framework значительно заимствует функционал класса ControllerTestCase, используемого для функциональных тестов с помощью PHPUnit. Он использует похожие подходы для загрузки и очистки. Для использования Zend Framework в ваших функциональных тестах включите модуль ZF1.

Пример для functional.suite.yml

class_name: TestGuy
modules:
    enabled: [ZF1, TestHelper] 

Смотрите полную справку здесь

Symfony

Этот модуль один из первых, который был разработан для Codeception. Поэтому его действия могут отличаться от действий, используемых в других фреймворках. Он предоставляет множество удобных операций, таких как логирование с помощью sfGuaruth или валидация внутри теста.

Пример для functional.suite.yml:

class_name: TestGuy
modules:
    enabled: [Symfony1, TestHelper] 

Смотрите полную справку здесь

Интеграция с другими фреймворками

Codeception не предоставляет базового модуля для функционального тестирования потому, что есть множество деталей, которые невозможно реализовать в таком варианте. Мы уже обсудили распространенные подводные камни функционального тестирования. И не существует единого способа решить все эти проблемы для всех PHP приложений. Так что, если Вы не используете ни один из фреймворков представленных выше, Вы можете интегрировать фреймворк, который используете в Codeception. Эта задача требует некоторого знания внутренностей Codeception и некоторого времени. Вероятно, Вам хватит использования только приемочных тестов, но любая помощь в расширении функциональности Codeception будет оценена. Разработчики Codeception-а посмотрят, что должно быть сделано для интеграции вашего фреймворка.

Использующеие HttpKernel

Если у Вас есть фреймворк, который использует Symfony's HttpKernel, работа с Codeception не составит труда. Вам нужно создать модуль для него и использовать его в Вашем приложении. У нас есть руководство по интеграции.

Разработайте модуль, попробуйте и поделитесь с сообществом.

Любые другие

Интеграция становится сложнее, если Ваш фреймворк не использует компонент HttpKernel. Самая сложная часть — это разрешение типичных проблем: управления памятью и использования headers функций. Codeception использует BrowserKit, взятый из компонентов Symfony для взаимодействия с приложениями в функциональных тестах. Этот компонент предоставляет все из распространенных действий, которые Вы могли видеть в методах click, fillField, see, и т.д. Таким образом, Вам не нужно реализовывать данные методы в своем модуле. Для интеграции Вы должны реализовать мост между BrowserKit и Вашим приложением.

Начнем с написания класса помощника для реализации интеграции:

<?php
namespace Codeception\Module;
class SomeFrameworkHelper extends \Codeception\Util\Framework {
     
}
?>

Давайте исследуем Исходный код Codeception. Взгляните в файл src/Util/Framework.php который мы расширяем. Он реализует большинство распространенных действий для всех фреймворков.

Как Вы можете заметить, все действия предоставляются объектом 'client'. Нам нужно создать client, который подключается к фреймворку. Client должен расширять модуль Symfony\BrowserKit\Client, примеры клиентов находятся в каталоге src/Util/Connector. Если фреймворк не предоставляет собственных компонентов для функционального тестирования, Вы можете попробовать использовать Universal коннектор. Или же взгляните как реализован Zend connector, и реализуйте свой собственный.

Решите ли Вы использовать Universal connector или напишете свой собственный, Вы можете включить его в свой модуль:

<?php
namespace Codeception\Module;
class SomeFrameworkHelper extends \Codeception\Util\Framework {
     
    public function _initialize() {
        $this->client = new \Codeception\Util\Connector\Universal();
        // or any other connector you implement
        
        // we need specify path to index file
        $this->client->setIndex('public_html/index.php');
    }     
}
?>

Если Вы включите данный помощник в Ваш набор, он позволит взаимодействовать с Вашими приложениями. Вы можете расширить его возможности, подключив внутренние функции фреймворка.

Важно выполнять очистку после каждого запуска теста!.

Это может быть реализовано в _before и _after методах модуля помощника. Проверьте, что фреймворк не кеширует никаких данных или конфигураций во время тестирования.

После того, как Вы сделаете модуль стабильным, поделитесь им с сообществом. Сделайте fork репозитория, добавьте Ваш модуль и сделайте Pull Request.

Вот некоторые требования к модулям:

  • Он должен легко конфигурироваться.
  • Он должен иметь хорошую документацию.
  • Он должен расширять базовые операции, используя возможности фреймворка.
  • Желательно, чтобы он мог выводить дополнительную отладочную информацию.

Мы не требуем включения модульных тестов, т.к. не существует хорошего пути для написания подходящих юнит тестов для интеграции фреймворков. Но Вы можете продемонстрировать простое приложение, использующее Ваш фремворк и Ваш модуль с Codeception.

Заключение

Функциональные тесты — отличная вещь, если Вы используете мощный фреймворк. Используя данные тесты, Вы можете получать доступ и управлять их внутренним состоянием. Это сделает Ваши тесты короче и быстрее. В случае, если Вы не используете фреймворки, практически нет смысла в написании функциональных тестов.

Если Вы используете фреймворк, которого нет в списке, напишите модуль и поделитесь с сообществом.

Трудились и переводили ребята из amyLabs

Если у вас есть вопросы или необходима помощь - задайте в чате - Yupe Team!