
Суть приемочных тестов в проверке того, что доступно любому человеку: просмотр содержимого страницы, попытка залогиниться и т.д.
Как я уже писал, существует два способа написания тестов в Codeception, которым соответствуют два типа наименования файлов: Cept и Cest. Рассмотрим их.
1. Пример создания приемочных тестов согласно функционального программирования (Cept).
Продублирую небольшой пример из первой статьи.
Файл теста можно сгенерировать автоматически. Создадим файл HomeCept.php командой:
codecept generate:cept acceptance HomeФайл появится в папке tests/acceptance и сразу будет содержать строки:
<?php $I = new AcceptanceTester($scenario); $I->wantTo('perform actions and see result');
При желании, создать файл теста можно и вручную, главное указать в названии файла окончание «Cept».
Создадим тест на проверку доступа к главной странице которая также должна вернуть указанный текст (фразу).
<?php $I = new AcceptanceTester($scenario); $I->wantTo('Проверить работу главной страницы.'); $I->amOnPage('/'); $I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); $I->see('Главная страница'); // Фраза или ее часть с главной страницыстоит учесть, что метод seeResponseCodeIs() будет работать при указании в конфигурационном файле модуля PHPBrowser и не будет работать с WebDriver.
2. Пример создания приемочных тестов согласно ООП (Cest).
Файл теста так же можно сгенерировать автоматически. Создадим файл HomeCest.php:
codecept generate:cest acceptance HomeФайл появится в папке tests/acceptance и сразу будет содержать указанные строки:
<?php class HomeCest { public function _before(AcceptanceTester $I) { } public function _after(AcceptanceTester $I) { } // tests public function tryToTest(AcceptanceTester $I) { } }
Перепишем пример согласно ООП, для этого нужно изменить только метод tryToTest():
public function tryToTest(AcceptanceTester $I) { $I->wantTo('Проверить работу главной страницы.'); $I->amOnPage('/'); $I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); $I->see('Главная страница'); // Фраза или ее часть с главной страницы }
Вы можете указывать свои произвольные названия методов тестов, но помните, что методы имеющие перед название нижнее подчеркивание не будут выполнены в качестве тестов.
Так же можете определить метод _failed() в классах Cest, который будет вызван в случае, если в тесте произошла ошибка error или он 'провалился'.
Запуск тестов.
Для запуска приемочных тестов отдельно от других типов тестов перейти в консоли в корень проекта или в каталог куда были установлены тесты (если установка была не в корень) и выполнить:codecept run acceptanceСогласно данного теста, главная страница сайта должна вернуть заголовок со статусом 200 и контент должен содержать фразу 'Главная страница'.
Следующий пример.
Усложним задачу. Допустим на нашем сайте есть страница контактов и нужно протестировать ее работу, а именно – перейти на нее используя ссылку в меню, заполнить форму, отправить, получить подтверждение об успешной отправке.<?php $I = new AcceptanceTester($scenario); $I->wantTo('go to the contact page and send the form'); $I->amOnPage('/'); //указываем текущую страницу (главная) $I->click('.menu li a:contains("Контакты")'); //кликаем для перехода на страницу контактов $I->see('ЗАДАВАЙТЕ ВОПРОСЫ', '#content'); //должна появиться указанная надпись в блоке с id "content" //Заполняем и сразу отправляем форму с id "contactForm" $I->submitForm("", ['name'=>'Serj', 'email'=>'serj@ukr.net', 'text'=>'Тестовое сообщение']); //должна появиться указанная надпись в блоке с class "alert-success" $I->see('Ваше сообщение отправлено.', '.alert-success');
Стоит отметить, что выборка элементов стандартным для JQuery образом, например:
$I->click('.menu li a:contains("Контакты")');будет работать при использовании PhpBrowser и не всегда будет работать с WebDriver (при использовании Selenium). Для WebDriver поиск элемента по его содержимому делается с использованием класса Locator и нужно писать так:
$I->click(Locator::contains('.menu li a', 'Контакты'));
Кроме того есть ограничения и для использования некоторых методов. Так, нельзя использовать метод submitForm().
Поэтому перепишем данный пример сделав его универсальным:
<?php use Codeception\Util\Locator; $I = new AcceptanceTester($scenario); $I->wantTo('go to the contact page and send the form'); $I->amOnPage('/'); //указываем текущую страницу $I->click(Locator::contains('.menu li a', 'Контакты')); //кликаем для перехода на страницу контактов $I->see('ЗАДАВАЙТЕ ВОПРОСЫ', '#content'); //должна появиться указанная надпись в блоке с id "content" $I->fillField('name','Serj'); $I->fillField('email','serj@ukr.net'); $I->fillField('text','Тестовое сообщение'); $I->click(Locator::contains('#contactForm button', 'Отправить')); //должна появиться указанная надпись в блоке с class "alert-success" $I->see('Ваше сообщение отправлено.', '.alert-success');
Запустить тесты можно так же с выводом всех шагов их выполнения:
codecept run acceptance --stepsДля последнего примера вывод в консоль будет такой:
Codeception PHP Testing Framework v2.3.6 Powered by PHPUnit 5.7.23 by Sebastian Bergmann and contributors. Acceptance Tests (1) ----------------------------------------------- ContactsCept: Perform actions and see result Signature: ContactsCept Test: tests\acceptance\ContactsCept.php Scenario -- I am on page "/" I click ".menu li a:contains("Контакты")" I see "ЗАДАВАЙТЕ ВОПРОСЫ","#content" I fill field "name","Serj" I fill field "email","serj@ukr.net" I fill field "text","Тестовое сообщение" I click "#contactForm button:contains("Отправить")" I see "Ваше сообщение отправлено.",".alert-success" PASSED --------------------------------------------------------------------
Если тест завершится с ошибкой, то удобно смотреть в каком тесте и на какой строке она возникла. Например вместо названия поля “text” я по-ошибке впишу “text2” и получим такую ошибку:

Указана причина ошибки: «Поле формы с помощью элемента Label или CSS с «text2» не было найдено».
Грабберы.
Грабберы это методы, которые используются для получения значений элементов и полей страницы, что может быть необходимо для использования в тесте.
Основные из них:
- получение текстового содержимого элемента:
$token = $I->grabTextFrom('.token');- получение значения поля:
$api_key = $I->grabValueFrom('input[name=api]');
Это может понадобиться, например, для ввода только что сгенерированного пароля в поле формы:
<?php $I->fillField('email','miles@davis.com') $I->click('Generate Password'); $password = $I->grabTextFrom('#password'); $I->click('Login'); $I->fillField('email','miles@davis.com'); $I->fillField('password', $password); $I->click('Log in!'); ?>
SELENIUM.
PhpBrowser не позволяет полноценно протестировать страницу на которой используется JavaScript, т.к. данный модуль использует библиотеку Curl. Для таких целей подключают модуль WebDriver, который использует Selenium.
Selenium — это проект, предоставляющий драйвера для разных браузеров, которые встраиваются в них и позволяют управлять ими. Также, Selenium содержит сервер, который позволяет управлять большим числом разных браузеров и распределять задания между ними. Selenium сервер написан на Яве, потому она понадобится чтобы его запустить.
При использовании Selenium для тестов будет запускаться браузер и вы увидите, как происходит заполнение полей форм, их отправка на сервер, клики по элементам и прочие действия, которые обычно совершаются пользователем приложения.
В примерах я буду использовать браузер Firefox, поэтому в начале указываю как настроить их взаимодействие с Selenium. Возможно так же использование браузера Google Chrome, пример настройки в конце статьи.
Есть несколько способов установки Selenium.
1. Самый простой и удобный с помощью Composer.
Страница расширения https://github.com/sveneisenschmidt/selenium-server-standaloneВыполнив команду:
composer global require --dev se/selenium-server-standaloneвы установите сервер Selenium глобально в папку текущего пользователя, у меня это C:\Users\Сергей\AppData\Roaming\Composer\vendor
Или с указанием конкретной версии:
composer global require --dev se/selenium-server-standalone 2.53.1Тут я указал версию 2.53.1, которая будет работать с версией Java, установленной сейчас на одном из моих компьютеров (обновить ее я не могу). Можно устанавливать без указания версии, в таком случае установится новейшая версия.
Стоит отметить что для использования данной версии Seleniumа (2.53.1), браузер Firefox должен быть ниже версии 48, например 47.0.1
Если вы устанавливаете новую версию Seleniumа (>=3), то потребуется и новая версия Firefox и что еще важно потребуется дополнительно загрузить geckodriver. Подробнее про него тут: http://barancev.github.io/geckodriver
Скачиваем данный драйвер https://github.com/mozilla/geckodriver/releases для своей операционной системы и сохраняем его в каталог указанный в системной переменной PATH. Я сохранил в D:\OpenServer 5.2.7\modules т.к. эта папка указана в списке. Или указываем нужный путь к каталогу с драйвером в переменной системной переменной PATH.
Запуск сервера Selenium осуществляется командой в консоли:
selenium-server-standalone
Если вы запускаете из Windows и у вас возникает такая ошибка:
/usr/bin/bash: "C:\Users\...\AppData\Roaming\Composer\vendor\bin\/../se/selenium-server-standalone/bin/selenium-server-standalone": No such file or directory
то нужно подкорректировать файл C:\Users\{USERNAME}\AppData\Roaming\Composer\vendor\bin\selenium-server-standalone.bat (тут показан путь при глобальной установке Selenium), заменить 2 последние строки на
SET BIN_TARGET=%~dp0/../se/selenium-server-standalone/bin/selenium-server-standalone.jar java -jar "%BIN_TARGET%" %*т.к. прописанная там по-умолчанию команда sh предназначена для выполнения на Unix подобных системах.
А если при выполнении команды возникнет ошибка такого плана:
Exception in thread "main" java.lang.UnsupportedClassVersionError: org/openqa/grid/selenium/GridLauncherV3 : Unsupported major.minor version 52.0
…
значит Selenium не может работать с установленной версией Java и нужно ее обновить или установить другую версию Selenium. В Win-7 для просмотра версии или обновления Java можно открыть в «Панели управления»:

Если все равно будут ошибки при запуске Selenium установите версию пониже.
Так же, при экспериментировании с версиями, уже при запуске тестов возможна ошибка такого плана:
[Facebook\WebDriver\Exception\UnknownServerException] Unable to connect to host 127.0.0.1 on port 7055 after 45000 ms.
…
Для устранения данной ошибки нужно обновить (или подобрать) версию Firefox с которой работает скаченный модуль Selenium. Например с selenium-server-standalone-2.53.1.jar будет работать с Firefox 47.0.1
2. Скачиваем и запускаем Selenium непосредственно.
Переходим на http://www.seleniumhq.org/download/ и нажимаем по ссылке после слов «Download version».Для того чтобы скачать предыдущие версии Selenium переходим по ссылке http://selenium-release.storage.googleapis.com/index.html и там скачиваем файл такого типа: selenium-server-standalone-X.X.X.jar
Далее, для использования Firefox скачиваем драйвер https://github.com/mozilla/geckodriver/releases для своей операционной системы и сохраняем его в каталог указанный в системной переменной PATH. Я сохранил в D:\OpenServer 5.2.7\modules, эта папка указана у меня в PATH. Вы можете указать нужный путь к каталогу с драйвером в переменной PATH. В эту же папку я переместил и скачанный модуль Selenium.
Установка описана в комментариях файла модуля - vendor\codeception\codeception\src\Codeception\Module\WebDriver.php
Перед выполнением тестирования должен быть запущен сервер. Запускаем командой:
java -jar -Dwebdriver.gecko.driver="D:\OpenServer 5.2.7\modules\geckodriver.exe" "D:\OpenServer 5.2.7\modules\selenium-server-standalone-3.4.0.jar"где нужно указать свои пути к скачанным файлам и в конце изменить версию Selenium.
Тут я указал путь к geckodriver.exe, но если он у вас лежит в папке которая указана в системной переменной PATH, то его можно не указывать. Получится:
java -jar "D:\OpenServer 5.2.7\modules\selenium-server-standalone-3.4.0.jar"
Так же вы можете создать файл selenium-server-standalone.bat в папке vendor\bin с указание своих путей к исполняемым файлам для удобства запуска модуля одной короткой строкой по примеру установки с помощью Composer.
Если возникнет ошибка примерно с таким содержанием:
[Facebook\WebDriver\Exception\SessionNotCreatedException] Unable to create new remote session. desired capabilities = Capabilities [{firefoxOptions=org.openqa.selenium.firefox.FirefoxOptions@7a99c504, browserName=firefox, moz:firefoxOptions=org.openqa.selenium.firefox.FirefoxOptions@7a99c504}], required capabilities = Capabilities [{}]
Build info: version: '3.0.0', revision: '350cf60', time: '2016-10-13 10:48:16 -0700'
System info: host: 'MICROSOFT-PC', ip: '192.168.56.1', os.name: 'Windows 7', os.arch: 'amd64', os.version: '6.1', java.version: '1.8.0_40'
Driver info: driver.version: FirefoxDriver
значит запускаемый файл Selenium не соответствует установленной версии Java и скорее всего придется попробовать Selenium более старой версии.
См. так же ошибки описанные в пункте установки с помощью Composer.
У меня работала такая связка:
Java 1.8.0_40 (1.8.0_51)
selenium-server-standalone-3.4.0.jar
и Firefox 56.0.2
хотя текущая на момент написания статьи была версия selenium-server-standalone-3.6.0.jar
Настройка модуля WebDriver (Selenium).
В файле tests/acceptance.suite.ymlactor: AcceptanceTester modules: enabled: - WebDriver: url: 'http://site.loc/' browser: 'firefox'тут:
- включаем модуль WebDriver;
- в “url” указать стартовый URL вашего сайта;
- в browser указываем браузер с которым будем работать 'chrome' или 'firefox'. Мы пишем 'firefox'.
Возможные настройки модуля WebDriver:
- url - Стартовый URL для вашего приложения.
- browser - Браузер для запуска.
- host - хост сервера Selenium (по умолчанию 127.0.0.1).
- port - порт сервера Selenium (по умолчанию 4444).
- restart - установите в «false» (по умолчанию) для использования одного и того же окна браузера для всех тестов или установите «true» для создания нового окна для каждого теста. В любом случае, когда все тесты завершены, окно браузера закрывается.
- start - автозапуск браузера для тестирования. Может быть отключен, если сеанс браузера начинается с «_initializeSession» внутри помощника.
- window_size - Начальный размер окна. Установите значение «maximize» или пропишите в формате «640x480».
- clear_cookies - Установите значение «false», чтобы сохранить файлы cookie, или установите значение «true» (по умолчанию), чтобы удалить все куки между тестами.
- wait (по умолчанию: 0 секунд) - всякий раз, когда элемент требуется и не находится на странице, подождите несколько секунд, чтобы найти его перед сбоем.
- capability - Настраивает Selenium [желаемые возможности] (https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities). Должен быть массив значений ключа.
- connection_timeout - тайм-аут для открытия соединения с удаленным сервером (по умолчанию 30 секунд).
- request_timeout - тайм-аут для запроса вернуть что-то из удаленного сервера selenium (по умолчанию 30 секунд).
- pageload_timeout - время ожидания загрузки страницы до того, как будет выдана ошибка (по умолчанию 0 секунд).
- http_proxy - устанавливает URL-адрес прокси-сервера HTTP для тестирования удаленного сервера.
- http_proxy_port - устанавливает порт HTTP-прокси-сервера
- debug_log_entries - сколько записей Selenium для печати с «debugWebDriverLogs» или при сбое (по умолчанию 15).
- log_js_errors - установите значение «true», чтобы включить возможный отчет JavaScript в HTML, или установите значение «false» (по умолчанию) для деактивации.
Подробнее можно посмотреть в блоке ## Configuration файла vendor\codeception\codeception\src\Codeception\Module\WebDriver.php
При использовании Selenium, метод see() позволяет не просто убедится, что текст внутри элемента существует, но также проверит, что элемент действительно виден пользователю.
<?php $I->see('Confirm','#modal'); ?>
Например на странице используется «Bootstrap Accordion» т.е. выводятся в ряд заголовки, кликнув по которым открывается блок соответствующего им текста. До клика по нужному заголовку, блок принадлежащего ему текста скрыт. При использовании PhpBrowser проверка текста из скрытого блока на то, что он в данное время не видим пользователю:
$I->dontSee('скрытый текст');завершится с ошибкой, т.к. PhpBrowser не умеет работать с скрытым текстом и он будет считаться видимым. А вот Selenium с этим справится.
Пример.
Для примера я использовал сайт со страницей на которой имеются скрытые блоки текста:<?php $I = new AcceptanceTester($scenario); $I->wantTo('go to Services page and click first block'); $I->amOnPage('/'); //начинаем с главной страницы $I->click(\Codeception\Util\Locator::contains('.menu li a', 'Услуги')); //кликаем для перехода на страницу услуг. $I->see('ПЕРЕЧЕНЬ УСЛУГ', '#content'); //должна появиться указанная надпись в блоке с id "content" //Проверяем, что блок невидим на странице $I->dontSee('Создание сайтов любой сложности'); $I->click(\Codeception\Util\Locator::contains('.panel-title a', ' Создание сайтов/приложений')); //кликаем для вывода содержимого блока на экран //Проверяем, что блок стал видимым $I->see('Создание сайтов любой сложности');По коду видно, что после перехода на страницу "Услуги" проверяется отсутствие надписи 'Создание сайтов любой сложности' из скрытого блока.
Далее, для показа содержимого скрытого блока, пользователь (а в нашем случае Selenium) кликает по соответствующей ссылке и мы проверяем, что текст из скрытого блока, а вернее его часть 'Создание сайтов любой сложности' стала видима.
Запуск тестов в браузере Chrome.
Для запуска тестов в браузере Chrome, так же как и при использовании Firefox, необходимо использовать специальный драйвер. Качаем архив по этой ссылке.Под номером релиза указаны версии браузера, которые он поддерживает. Поэтому проверяем свою версию или просто обновляем браузер и скачиваем новейший релиз драйвера.
Из архива извлекаем файл соответствующий вашей операционной системе и указываем путь к каталогу с драйвером (куда вы его поместили) в переменной окружения PATH так же как и при использовании Firefox.
В конфигурационном файле tests/acceptance.suite.yml указываем url и браузер:
actor: AcceptanceTester modules: enabled: - WebDriver: url: 'http://site.loc/' browser: 'chrome'Далее запускаем Selenium и после этого выполнение тестов.
Создание отдельного класса для работы со страницей.
Функционал Codeception позволяет вынести действия по взаимодействию с определенной страницей в отдельный класс. Это бывает удобно, т.к. во-первых данный класс могут в дальнейшем использовать несколько отдельных тестов, кроме того сами тесты становятся более читаемыми.
Пример.
Допустим нам нужно создать класс, который бы выполнял переход на страницу входа пользователей и логинился (вводил регистрационные данные). А дальше, например, скрипт переадресует пользователя на страницу текущего пользователя, которую и нужно протестировать.
Т.е. тестировать нам нужно страницу авторизованного пользователя, но сначала нужно «залогиниться», т.е. приходится взаимодействовать с страницей входа, которая нас не интересует. Вот ее и вынесем в отдельный класс.
Создадим класс для работы со страницей “login” (http:site.com/login).
codecept generate:pageobject LoginPage
Появится файл tests/_support/Page/LoginPage.php. Нам потребуется такой функционал:
<?php namespace Page; class LoginPage { // include url of current page public static $URL = 'login'; protected $actor; public static function route($param) { return static::$URL.$param; } public function __construct($I) { $this->actor = $I; } public function open() { $I = $this->actor; $I->amOnPage(self::$URL); } public function enter($login, $password) { $I = $this->actor; $I->fillField('#loginform-username', $login); $I->fillField('#loginform-password', $password); $I->click('button[name=login-button]'); } }
Теперь в тесте той страницы, к которой есть доступ только у зарегистрированных пользователей, можно обращаться к данному классу так:
public function registeredUser (AcceptanceTester $I) { $I->wantTo('проверить страницу зарегистрированного пользователя'); $page = new \Page\LoginPage($I); $page ->open(); $I->see('Вход'); $page->enter('MySecretName', '1111111111'); $I->see('Личный кабинет'); ...
При желании, можно вообще вместо
$page = new \Page\LoginPage($I); $page ->open(); $I->see('Вход'); $page->enter('MySecretName', '1111111111');сделать все одним вызовом в тесте:
\Page\LoginPage::open($I);чтобы все действия по авторизации пользователя находились в классе LoginPage. Тогда код данного класса (файл tests/_support/Page/LoginPage.php) будет такой:
<?php namespace Page; class LoginPage { // include url of current page public static $URL = 'login'; protected $actor; public static $login = 'MySecretName'; public static $password = '111111111'; public static function route($param) { return static::$URL.$param; } public function __construct($I) { $this->actor = $I; } public static function open($I) { $page = new static($I); $I->amOnPage(self::$URL); $page->enter(self::$login, self::$password); } public function enter($login, $password) { $I = $this->actor; $I->fillField('#loginform-username', $login); $I->fillField('#loginform-password', $password); $I->click('button[name=login-button]'); } }
Статичный интерфейс метода open() служит для удоства, чтобы повесить создание объекта класса LoginPage на его же метод. Мы передаем конструктору данного класса переменную I, являющуюся экземпляром класса AcceptanceTester для того, чтобы мы могли выполнить действия такие же как в самом тесте (переходы по страницам, заполнение полей и тд.)
Дополнения.
Справка по методам приемочного тестирования с примерами находится в файле vendor\codeception\codeception\docs\03-AcceptanceTests.md
Вот некоторые:
// отправить AJAX запрос: $I->sendAjaxPostRequest('/path', ['param' => 'value']);
// проверка на то, что текущий URL «/user/view»: $I->seeInCurrentUrl('/user/view');
// проверка существования указанной ссылки. В качестве первого параметру методу передается текстовое содержимое ссылки, в качестве второго можно передать URL для уточнения: $I->seeLink('Услуги'); $I->seeLink('Услуги','/services');
// проверяем отсутствие текста на странице $I->dontSee('Form is filled incorrectly');
// проверка на включенный чекбокс: $I->seeCheckboxIsChecked('#agree');
// наличие указанного текста в поле формы: $I->seeInField('user[name]','Miles');
//проверяет, что заголовок страницы содержит заданную строку. $I->seeInTitle('Login');
// проверяет, что заголовок страницы не содержит заданную строку. $I->dontSeeInTitle('Control panel');
// добавить текст в поле $I->appendField('#pass', 'pass');
// нажать клавишу $I->pressKey('.css', WebDriverKeys::ENTER);
Пример тестирования файловой системы:
$I->amInPath('tests/data/sandbox'); $I->executeCommand('run order --no-exit'); $I->seeFileFound('order.txt', 'tests/_log'); $I->seeInThisFile("IBSBSBS([BST][BSTF][BST])");все методы для работы с файловой системой с примерами можно посмотреть в файле vendor\codeception\codeception\tests\data\included\shire\tests\functional\TestGuy.php
Использование полезных функций (класс Locator) для работы с выборкой элементов со страницы.
Данные методы можно посмотреть в файле vendor\codeception\codeception\src\Codeception\Util\Locator.php, тут укажу только самые полезные, на мой взгляд.
Подключаем класс Locator в начале тестирующего класса:
use Codeception\Util\Locator;Отбираем элемент для клика по нему
- по произвольному атрибуту элемента:
- отдельно для ссылок:
- отбор по содержимому элемента:
Проверка существования указанного селектора на странице:
Locator::isCSS('#user .hello')
Данная статья является одной из серии статей про фреймворк для тестирования Codeception. Читайте так же:
- Установка, настройка и базовое использование фреймворка для тестирования "Codeception".
- Codeception - взаимодействие с базой данных. Модуль Db, фикстуры.
- Codeception - модульное (unit) тестирование. Создание имитирующих объектов.
MakeScreenshot делать скрин только видимой области
ответ на комментарий Илья от 31.05.2018