Spec-Zone.ru › Codeception

Модули и помощники

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

Все действия и утверждения, которые могут выполняться объектом Tester в классе, определены в модулях. Вы можете расширить набор тестов собственными действиями и утверждениями, написав их в пользовательском модуле.

Давайте рассмотрим следующий тест:

<?php
$I = new FunctionalTester($scenario);
$I->amOnPage('/');
$I->see('Hello');
$I->seeInDatabase('users', array('id' => 1));
$I->seeFileFound('running.lock');

Он может работать с различными сущностями: веб-страница может быть загружена с помощью модуля PhpBrowser, утверждения для базы данных используют модуль Db, а состояние файла может быть проверено с помощью модуля Filesystem.

Модули присоединяются к классам Актера в конфигурации набора. Например, в tests/functional.suite.yml мы должны увидеть:

actor: FunctionalTester
modules:
    enabled:
        - PhpBrowser:
            url: http://localhost
        - Db:
            dsn: "mysql:host=localhost;dbname=testdb"
        - Filesystem

Класс FunctionalTester имеет свои методы, определенные в модулях. На самом деле, он не содержит их, а скорее действует как прокси. Он знает, какой модуль выполняет это действие и передает параметры в него. Чтобы ваш IDE видел все методы FunctionalTester, вы должны запустить команду codecept build. Она генерирует сигнатуры методов из включенных модулей и сохраняет их в трайт, который включён в актёра. В текущем примере будет сгенерирован файл tests/support/_generated/FunctionalTesterActions.php. По умолчанию Codeception автоматически перестраивает трайт Actions при каждом изменении конфигурации набора.

Стандартные модули

Codeception имеет множество интегрированных модулей, которые помогут вам запускать тесты для различных целей и сред. Идея модулей заключается в совместном использовании общих действий, чтобы разработчики и специалисты по контролю качества могли сосредоточиться на тестировании, а не на изобретении велосипеда. Каждый модуль предоставляет методы для тестирования своей части, а объединение модулей позволяет получить мощную настройку для тестирования приложения на всех уровнях.

Существует модуль WebDriver для приёмочного тестирования, модули для всех популярных фреймворков PHP, PHPBrowser для эмуляции работы браузера, REST для тестирования API и многое другое. Модули считаются самой ценной частью Codeception. Они постоянно совершенствуются, чтобы обеспечить наилучший опыт тестирования и быть гибкими для удовлетворения потребностей всех.

Конфликты модулей

Модули могут конфликтовать друг с другом. Если модуль реализует Codeception\Lib\Interfaces\ConflictsWithModule, он может объявить правило конфликта для использования с другими модулями. Например, WebDriver конфликтует со всеми модулями, реализующими интерфейс Codeception\Lib\Interfaces\Web.

public function _conflicts()
{
    return 'Codeception\Lib\Interfaces\Web';
}

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

Чтобы избежать путаницы, модули фреймворка, PhpBrowser и WebDriver не могут использоваться вместе. Например, метод amOnPage существует во всех этих модулях, и вам не следует пытаться угадать, какой модуль фактически его выполнит. Если вы выполняете приёмочное тестирование, настройте либо WebDriver, либо PHPBrowser, но не оба одновременно. Если вы выполняете функциональное тестирование, включите только один модуль фреймворка.

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

modules:
    enabled:
        - WebDriver:
            browser: firefox
            url: http://localhost
        - REST:
            url: http://localhost/api/v1
            depends: PhpBrowser

Эта конфигурация позволит вам отправлять запросы GET/POST к API сервера, работая с сайтом через браузер.

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

Части модулей

Модули с разделом Части в их справочнике могут загружаться частично. Таким образом, объект $I будет иметь действия, принадлежащие только определённой части этого модуля. Частично загруженные модули также могут использоваться для избегания конфликтов модулей.

Например, модуль Laravel5 имеет часть ORM, которая содержит действия базы данных. Вы можете включить модуль PhpBrowser для тестирования и Laravel + ORM для подключения к базе данных и проверки данных.

modules:
    enabled:
        - PhpBrowser:
            url: http://localhost
        - Laravel5:
            part: ORM

Модули не будут конфликтовать, так как действия с одинаковыми именами не будут загружаться.

Модуль REST имеет части для Xml и Json аналогичным образом. Если вы тестируете REST-сервис только с ответами в формате JSON, вы можете включить только часть JSON этого модуля:

actor: ApiTester
modules:
    enabled:
        - REST:
            url: http://serviceapp/api/v1/
            depends: PhpBrowser
            part: Json

Помощники

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

Выполнив команду bootstrap, Codeception сгенерирует три пустых модуля для каждого из вновь созданных наборов. Эти пользовательские модули называются «Помощниками», и их можно найти в каталоге tests/_support.

<?php
namespace Helper;
// here you can define custom functions for FunctionalTester

class Functional extends \Codeception\Module
{
}

Действия также довольно просты. Каждое определённое вами действие – это публичный метод. Напишите публичный метод, затем запустите команду build, и вы увидите новый метод, добавленный в класс FunctionalTester.

Публичные методы, начинающиеся с `_`, обрабатываются как скрытые и не будут добавлены в ваш класс Актера.

Утверждения могут быть немного сложнее. Во-первых, рекомендуется добавлять в префикс всех ваших утверждений see или dontSee.

Называйте утверждения так:

<?php
$I->seePageReloaded();
$I->seeClassIsLoaded($classname);
$I->dontSeeUserExist($user);

И затем используйте их в ваших тестах:

<?php
$I->seePageReloaded();
$I->seeClassIsLoaded('FunctionalTester');
$I->dontSeeUserExist($user);

Вы можете определять утверждения, используя методы assertXXX в своих модулях.

<?php

function seeClassExist($class)
{
    $this->assertTrue(class_exists($class));
}

В ваших помощниках вы можете использовать эти утверждения:

<?php

function seeCanCheckEverything($thing)
{
    $this->assertTrue(isset($thing), "this thing is set");
    $this->assertFalse(empty($any), "this thing is not empty");
    $this->assertNotNull($thing, "this thing is not null");
    $this->assertContains("world", $thing, "this thing contains 'world'");
    $this->assertNotContains("bye", $thing, "this thing doesn't contain 'bye'");
    $this->assertEquals("hello world", $thing, "this thing is 'Hello world'!");
    // ...
}

Доступ к другим модулям

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

Модули могут взаимодействовать друг с другом через метод getModule. Обратите внимание, что этот метод выбросит исключение, если необходимый модуль не был загружен.

Представим, что мы пишем модуль, который переподключается к базе данных. Он должен использовать значение соединения dbh из модуля Db.

<?php

function reconnectToDatabase()
{
    $dbh = $this->getModule('Db')->dbh;
    $dbh->close();
    $dbh->open();
}

Используя функцию getModule, вы получаете доступ ко всем публичным методам и свойствам запрошенного модуля. Свойство dbh было определено как публичное специально для доступности другим модулям.

Модули также могут содержать методы, доступные для использования в классах помощников. Эти методы начинаются с префикса _ и недоступны в классах Актера, поэтому к ним можно получить доступ только из модулей и расширений.

Вы должны использовать их для написания собственных действий с использованием внутренних компонентов модуля.

<?php
function seeNumResults($num)
{
    // retrieving webdriver session
    /**@var $table \Facebook\WebDriver\WebDriverElement */
    $elements = $this->getModule('WebDriver')->_findElements('#result');
    $this->assertNotEmpty($elements);
    $table = reset($elements);
    $this->assertEquals('table', $table->getTagName());
    $results = $table->findElements('tr');
    // asserting that table contains exactly $num rows
    $this->assertEquals($num, count($results));
}

В этом примере мы используем API библиотеки facebook/php-webdriver, клиента Selenium WebDriver, на котором построен модуль. Вы также можете получить доступ к свойству webDriver модуля, чтобы получить доступ к экземпляру Facebook\WebDriver\RemoteWebDriver для прямого взаимодействия с Selenium.

Расширение модуля

Если доступ к модулям не обеспечивает достаточной гибкости, вы можете расширить модуль внутри класса Помощника:

<?php
namespace Helper;

class MyExtendedSelenium extends \Codeception\Module\WebDriver
{
}

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

Хуки

Каждый модуль может обрабатывать события, возникающие во время выполнения теста. Модуль может быть выполнен до начала теста или после его завершения. Это может быть полезно для действий инициализации/очистки. Вы также можете определить специальное поведение, когда тест завершается неудачей. Это может помочь в отладке проблемы. Например, модуль PhpBrowser сохраняет текущую веб-страницу в каталог tests/_output, когда тест завершается неудачей.

Все хуки определены в Codeception\Module и перечислены здесь. Вы можете их переопределять в своём модуле.

<?php

// HOOK: used after configuration is loaded
public function _initialize()
{
}

// HOOK: before each suite
public function _beforeSuite($settings = array())
{
}

// HOOK: after suite
public function _afterSuite()
{
}

// HOOK: before each step
public function _beforeStep(\Codeception\Step $step)
{
}

// HOOK: after each step
public function _afterStep(\Codeception\Step $step)
{
}

// HOOK: before test
public function _before(\Codeception\TestInterface $test)
{
}

// HOOK: after test
public function _after(\Codeception\TestInterface $test)
{
}

// HOOK: on fail
public function _failed(\Codeception\TestInterface $test, $fail)
{
}

Обратите внимание, что методы с префиксом _ не добавляются в класс Актера. Это позволяет их определять как публичные, но использовать только во внутренних целях.

Отладка

Как мы уже упоминали, хук _failed может помочь в отладке неудачного теста. У вас есть возможность сохранить текущее состояние теста и показать его пользователю, но вы не ограничены этим.

Каждый модуль может выводить внутренние значения, которые могут быть полезны во время отладки. Например, модуль PhpBrowser выводит код ответа и текущий URL каждый раз, когда переходит на новую страницу. Таким образом, модули не являются чёрными ящиками. Они пытаются показать вам, что происходит во время теста. Это делает отладку ваших тестов менее болезненной.

Для отображения дополнительной информации используйте методы debug и debugSection модуля. Вот пример того, как это работает для PhpBrowser:

<?php
$this->debugSection('Request', $params);
$this->client->request($method, $uri, $params);
$this->debug('Response Code: ' . $this->client->getStatusCode());

Этот тест, запущенный с модулем PhpBrowser в режиме отладки, выведет что-то вроде этого:

I click "All pages"
* Request (GET) http://localhost/pages {}
* Response code: 200

Конфигурация

Модули и помощники могут быть сконфигурированы из файла конфигурации набора или глобально из codeception.yml.

Обязательные параметры должны быть определены в свойстве $requiredFields класса. Вот как это сделано в модуле Db:

<?php
class Db extends \Codeception\Module
{
    protected $requiredFields = ['dsn', 'user', 'password'];
    // ...
}

В следующий раз, когда вы запустите набор без установки одного из этих значений, будет выброшено исключение.

Для необязательных параметров вы должны установить значения по умолчанию. Свойство $config используется для определения необязательных параметров и их значений. В модуле WebDriver мы используем адрес и порт Selenium Server по умолчанию.

<?php
class WebDriver extends \Codeception\Module
{
    protected $requiredFields = ['browser', 'url'];
    protected $config = ['host' => '127.0.0.1', 'port' => '4444'];
    // ...
}

Параметр хоста и порта можно переопределить в конфигурации набора. Значения устанавливаются в разделе modules:config файла конфигурации.

modules:
    enabled:
        - WebDriver:
            url: 'http://mysite.com/'
            browser: 'firefox'
        - Db:
            cleanup: false
            repopulate: false

К необязательным и обязательным параметрам можно получить доступ через свойство $config. Используйте $this->config['parameter'] для получения его значения.

Динамическая конфигурация с параметрами

Модули могут быть динамически сконфигурированы из переменных среды. Хранение параметров должно быть указано в глобальной конфигурации codeception.yml внутри раздела params. Параметры могут загружаться из переменных среды, из YAML (формат Symfony), .env (формат Laravel), ini или php файлов.

Используйте раздел params файла глобальной конфигурации codeception.yml, чтобы указать, как загрузить их. Вы можете указать несколько источников для загрузки параметров.

Пример: загрузка параметров из переменных среды:

params:
    - env # load params from environment vars

Пример: загрузка параметров из YAML-файла (Symfony):

params:
    - app/config/parameters.yml

Пример: загрузка параметров из php-файла (Yii):

params:
    - config/params.php

Пример: загрузка параметров из .env-файлов (Laravel):

params:
    - .env
    - .env.testing

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

Допустим, мы хотим указать учетные данные для облачной тестовой службы. Мы загрузили переменные SAUCE_USER и SAUCE_KEY из среды, и теперь передаём их значения в конфигурацию WebDriver:

modules:
   enabled:
      - WebDriver:
         url: http://mysite.com
         host: '%SAUCE_USER%:%SAUCE_KEY%@ondemand.saucelabs.com'

Параметры также полезны для предоставления учетных данных подключения для модуля Db (взятые из файлов .env Laravel):

modules:
    enabled:
        - Db:
            dsn: "mysql:host=%DB_HOST%;dbname=%DB_DATABASE%"
            user: "%DB_USERNAME%"
            password: "%DB_PASSWORD%"

Конфигурация во время выполнения

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

В этом примере мы изменяем корневой URL для PhpBrowser, так что amOnPage('/') откроет /admin/.

<?php
$this->getModule('PhpBrowser')->_reconfigure(['url' => 'http://localhost/admin']);

Обычно эти изменения конфигурации вступают в силу немедленно. Однако в конфигурации WebDriver изменения не могут быть применены так просто. Например, если вы измените браузер, вам необходимо закрыть текущую сессию браузера и запустить новую. Для этого модуль WebDriver предоставляет метод _restart, который принимает массив конфигурации и перезапускает браузер:

<?php
// start chrome
$this->getModule('WebDriver')->_restart(['browser' => 'chrome']);
// or just restart browser
$this->getModule('WebDriver')->_restart();

В конце теста все изменения конфигурации будут отменены и восстановлены к исходным значениям.

Конфигурация во время выполнения теста

Иногда требуется установить пользовательскую конфигурацию только для конкретного теста. Для форматов Cest и Test\Unit вы можете использовать аннотацию @prepare, которая может выполнить код перед выполнением других хуков. Это позволяет @prepare изменить конфигурацию модуля во время выполнения. @prepare использует инъекцию зависимостей для автоматической инъекции необходимых модулей в метод.

Чтобы запустить конкретный тест только в браузере Chrome, вы можете вызвать _reconfigure из модуля WebDriver для самого теста, используя @prepare.

<?php
/**
 * @prepare useChrome
 */
public function chromeSpecificTest()
{
    // ...
}

protected function useChrome(\Codeception\Module\WebDriver $webdriver)
{
    // WebDriver was injected by the class name
    $webdriver->_reconfigure(['browser' => 'chrome']);
}

Методы подготовки могут вызывать все методы модуля, а также скрытые API-методы (начинающиеся с _). Используйте их для настройки модуля для конкретного теста.

Чтобы изменить конфигурацию модуля для определённой группы тестов, используйте GroupObjects.

Заключение

Модули — это настоящая мощь Codeception. Они используются для эмуляции множественного наследования для классов Actor (UnitTester, FunctionalTester, AcceptanceTester и т. д.). Codeception предоставляет модули для эмуляции веб-запросов, доступа к данным, взаимодействия с популярными PHP-библиотеками и т. д. Если встроенных модулей недостаточно, это нормально — вы можете написать свои собственные! Используйте помощники (пользовательские модули) для всего, что Codeception не может сделать из коробки. Помощники также могут быть использованы для расширения функциональности исходных модулей.

  • Следующая глава: ReusingTestCode >
  • Предыдущая глава: < UnitTests

© 2011 Michael Bodnarchuk and contributors
Licensed under the MIT License.
https://codeception.com/docs/06-ModulesAndHelpers

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API