Статьи

Программирование с помощью Yii2: автоматизированное тестирование с помощью Codeception

Конечный продукт
Что вы будете создавать

Если вы спрашиваете: «Что такое Yii?» Ознакомьтесь с моим предыдущим руководством « Введение в Yii Framework» , в котором рассматриваются преимущества Yii и представлен обзор того, что нового в Yii 2.0, выпущенном в октябре 2014 года.

В этой серии «Программирование с Yii2» я расскажу читателям, как использовать Yii2 Framework для PHP. В этом руководстве я расскажу об автоматическом тестировании с использованием Codeception , интегрированного в среду разработки Yii2.

По общему признанию, мой опыт написания тестов с моим кодом скуден. Я часто был частью сольных или небольших проектов с ограниченными ресурсами. Во время моего пребывания в Microsoft у нас были разные команды тестирования, которые делали это. Но, честно говоря, это, скорее всего, типично и для вас, верно? Программисты любят кодировать, они не пишут тесты — по крайней мере, программисты старой школы этого не делали.

Codeception — это инновационная библиотека, которая в буквальном смысле призвана сделать написание тестов веселым и простым. И я бы сказал, что они достигли разумной степени. Когда я опустил палец в воду «Озерной кодекса», это было в основном легко и весело. Однако когда я начал углубляться, у меня возникли проблемы с настройкой Yii и конкретных модулей, используемых в этой серии. Были определенно проблемы. В целом, тем не менее, я впечатлен и вижу пользу обучения больше.

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

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

Для руководства я использовал документацию по настройке среды тестирования Yii . Я начал с глобальной установки кода, чтобы использовать его в любом проекте.

01
02
03
04
05
06
07
08
09
10
11
$ composer global require «codeception/codeception=2.1.*»
Changed current directory to /Users/Jeff/.composer
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  — Installing symfony/yaml (v3.1.1)
    Loading from cache
codeception/codeception suggests installing symfony/phpunit-bridge (For phpunit-bridge support)
Writing lock file
Generating autoload files

Вы также должны требовать codeception/specify :

01
02
03
04
05
06
07
08
09
10
11
$ composer global require «codeception/specify=*»
Changed current directory to /Users/Jeff/.composer
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
composer require «codeception/verify=*»
  — Installing codeception/specify (0.4.3)
    Downloading: 100%
 
Writing lock file
Generating autoload files

И codeception/verify :

1
2
3
4
5
6
7
8
9
$ composer require «codeception/verify=*»
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
  — Installing codeception/verify (0.3.0)
    Downloading: 100%
 
Writing lock file
Generating autoload files

Далее, это помогает настроить псевдоним для codecept используя ваш глобальный каталог composer:

1
2
3
$ composer global status
Changed current directory to /Users/Jeff/.composer
No local changes

Это устанавливает псевдоним:

1
$ alias codecept=»/Users/Jeff/.composer/vendor/bin/codecept»

Yii также требует установки Faker , который генерирует поддельные данные тестирования для вашего приложения:

1
2
3
4
5
6
$ composer require —dev yiisoft/yii2-faker:*
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files

Codecept bootstrap   инициализирует кодирование для вашего приложения Yii, создавая различные файлы конфигурации для создания и запуска тестов для вашего приложения. Мы используем приложение Hello из этой серии для этого урока. Смотрите ссылку на GitHub на этой странице, чтобы получить код.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$ codecept bootstrap
 Initializing Codeception in /Users/Jeff/Sites/hello
 
File codeception.yml created <- global configuration
tests/unit created <- unit tests
tests/unit.suite.yml written <- unit tests suite configuration
tests/functional created <- functional tests
tests/functional.suite.yml written <- functional tests suite configuration
tests/acceptance created <- acceptance tests
tests/acceptance.suite.yml written <- acceptance tests suite configuration
tests/_output was added to .gitignore
 —
tests/_bootstrap.php written <- global bootstrap file
Building initial Tester classes
Building Actor classes for suites: acceptance, functional, unit
 -> AcceptanceTesterActions.php generated successfully.
\AcceptanceTester includes modules: PhpBrowser, \Helper\Acceptance
AcceptanceTester.php created.
 -> FunctionalTesterActions.php generated successfully.
\FunctionalTester includes modules: \Helper\Functional
FunctionalTester.php created.
 -> UnitTesterActions.php generated successfully.
\UnitTester includes modules: Asserts, \Helper\Unit
UnitTester.php created.
 
Bootstrap is done.

По какой-то причине я также получил дубликаты каталогов тестирования в hello / tests; просто удалив hello / tests / function, hello / tests / accept и hello / tests / unit все прояснилось. Все тесты живут в hello / tests / codeception / *.

Codeception ориентирован на три вида тестов:

  1. Модульное тестирование проверяет работоспособность определенных модулей, например, исчерпывающее тестирование всех методов вашей модели.
  2. Функциональное тестирование проверяет общие сценарии применения, как если бы пользователь их исполнял, но с помощью эмуляции веб-браузера.
  3. Приемочное тестирование идентично функциональному тестированию, но фактически запускает тесты через веб-браузер.

И он поддерживает три различных формата тестовых форматов для вашего тестового кода:

  1. Cept: это самый простой тестовый файл для одного сценария
  2. Cest: объектно-ориентированный формат для запуска нескольких тестов в одном файле
  3. Test: тесты, написанные на PHPUnit, среде тестирования PHP

Начнем с примера приемочных испытаний в формате cept :

Мы будем использовать приветствие Codeception   Тестовый пример первым.

1
2
$ codecept generate:cept acceptance Welcome
Test was created in /Users/Jeff/Sites/hello/tests/acceptance/WelcomeCept.php

Это создает tests/acceptance/WelcomeCept.php , которые мы будем редактировать ниже.

Поскольку для приемочных испытаний требуется браузер, мы должны отредактировать файл /tests/acceptance.suite.yml в нашем проекте, чтобы предоставить наш URL-адрес для разработки, http: // localhost: 8888 / hello :

01
02
03
04
05
06
07
08
09
10
11
12
# Codeception Test Suite Configuration
#
# Suite for acceptance tests.
# Perform tests in browser using the WebDriver or PhpBrowser.
# If you need both WebDriver and PHPBrowser tests — create a separate suite.
 
class_name: AcceptanceTester
modules:
    enabled:
        — PhpBrowser:
            url: http://localhost:8888/hello/
        — \Helper\Acceptance

Теперь мы готовы изменить первоначальный тест в файле tests / accept / WelcomeCept.php. Я пишу тест, который загружает первую страницу, чтобы убедиться, что она работает как положено.

Тесты Codeception имеют концепцию актера, в данном случае $I = new AcceptanceTester() .

Вот как это описывает актеров в документации Codeception:

У нас есть UnitTester, который выполняет функции и тестирует код. У нас также есть FunctionalTester, квалифицированный тестировщик, который тестирует приложение в целом со знанием его внутренних компонентов. И AcceptanceTester, пользователь, который работает с нашим приложением через интерфейс, который мы предоставляем.

Вы можете комментировать свои тесты с помощью кода, такого как $I->wantTo('perform a certain test')   или 'ensure that the frontpage works' .

В моем тесте я хочу увидеть $I->see Текст 'Congratulations!' и 'Yii-powered' :

1
2
3
4
5
6
<?php
$I = new AcceptanceTester($scenario);
$I->wantTo(‘ensure that frontpage works’);
$I->amOnPage(‘/’);
$I->see(‘Congratulations!’);
$I->see(‘Yii-powered’);

Вот текущая домашняя страница Hello:

Программирование с Yii - Привет, домашняя страница

Далее давайте запустим тест, просто codecept run :

01
02
03
04
05
06
07
08
09
10
11
12
$ codecept run
Codeception PHP Testing Framework v2.1.11
Powered by PHPUnit 5.3.5 by Sebastian Bergmann and contributors.
Acceptance Tests (1) —————————————————————————————
Ensure that frontpage works (WelcomeCept) Ok
————————————————————————————————————
Functional Tests (0) ————————
———————————————
Unit Tests (0) ——————————
———————————————
Time: 554 ms, Memory: 10.25MB
OK (1 test, 2 assertions)

Как видите, наш тест пройден, и код для проверки этой функциональности был довольно читабельным и простым.

Чтобы пойти дальше, я начал использовать стандартные тесты Yii. На этом этапе я столкнулся с рядом проблем конфигурации — большинство из-за моего использования пользовательского модуля yii2-user в этой серии. Другие были связаны с небольшими ошибками в Yii, на которые его команда быстро реагировала и исправляла, когда сообщала о них на GitHub; в некоторых случаях проблемы были исправлены в более поздних выпусках дерева yii2-basic.

Кроме того, поскольку я обновил базовое дерево yii2 для этой серии, мне пришлось внести небольшие изменения в некоторые тесты по умолчанию.

Вот пример выходных данных для запуска приемочных тестов по умолчанию после того, как я сделал некоторые незначительные корректировки:

01
02
03
04
05
06
07
08
09
10
$ codecept run
Codeception PHP Testing Framework v2.1.11
Powered by PHPUnit 5.3.5 by Sebastian Bergmann and contributors.
 
Acceptance Tests (4) ————————————————————————————————-
Ensure that about works (AboutCept) Ok
Ensure that contact works (ContactCept) Ok
Ensure that home page works (HomeCept) Ok
Ensure that login works (LoginCept) Ok
———————————————————————————————————————-

Чтобы заставить работать функциональные тесты, мне нужно было запустить экземпляр встроенного сервера Yii . Я не знал об этом компоненте, пока Алекс Макаров из Yii не упомянул об этом на нашей бирже GitHub.

1
$ ./yii serve

Я внес небольшие изменения в функциональные тесты в / tests / codeception / functions, главным образом для поиска моих конкретных обновленных текстовых строк, то есть «Неверный логин или пароль» вместо значения по умолчанию в Yii. Вот посмотрите на LoginCept.php:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
use tests\codeception\_pages\LoginPage;
$I = new FunctionalTester($scenario);
 
$I->wantTo(‘ensure that login works’);
 
$loginPage = LoginPage::openBy($I);
$I->see(‘Login’);
$I->amGoingTo(‘try to login with empty credentials’);
$loginPage->login(», »);
$I->expectTo(‘see validations errors’);
$I->see(‘Login cannot be blank.’);
$I->see(‘Password cannot be blank.’);
 
$I->amGoingTo(‘try to login with wrong credentials’);
$loginPage->login(‘admin’, ‘wrong’);
$I->expectTo(‘see validations errors’);
$I->see(‘Invalid login or password’);
 
$I->amGoingTo(‘try to login with correct credentials’);
$loginPage->login(‘admin’, ‘admin11’);
$I->expectTo(‘see user info’);
$I->see(‘Logout’);

По сути, код обращается к модели LoginForm и тестирует ее различные методы, используя Yii serve.

Вот код тестирования /tests/codeception_pages/LoginPage.php, который он использует (мне также пришлось изменить его для изменений, которые мы внесли в серию):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
class LoginPage extends BasePage
{
    public $route = ‘user/login’;
 
    /**
     * @param string $username
     * @param string $password
     */
    public function login($username, $password)
    {
        $this->actor->fillField(‘input[name=»login-form[login]»]’, $username);
        $this->actor->fillField(‘input[name=»login-form[password]»]’, $password);
        $this->actor->click(‘button[type=submit]’);
    }
}

Вы можете видеть, что мы кодируем актера в fillFields и click кнопки для наших обновленных полей формы.

Во время устранения неполадок интеграции Codeception с Yii было полезно запустить эти тесты в подробном режиме:

1
$ codecept run -vvv

Вот подробный вывод из функциональных тестов входа в систему — в MacOS Terminal, PASSED и FAILED кодируются красным или розовым цветом и инвертируются для наглядности:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Functional Tests (4) ————————————————————————————————-
Modules: Filesystem, Yii2
———————————————————————————————————————-
Ensure that login works (LoginCept)
Scenario:
* I am on page «/index-test.php/user/login»
 
  [Page] /index-test.php/user/login
  [Response] 200
  [Request Cookies] []
  [Response Headers] {«content-type»:[«text/html; charset=UTF-8»]}
* I see «Login»
* I am going to try to login with empty credentials
* I fill field «input[name=»login-form[login]»]»,»»
* I fill field «input[name=»login-form[password]»]»,»»
* I click «button[type=submit]»
  [Uri] http://localhost/index-test.php/user/login
  [Method] POST
  [Parameters] {«_csrf»:»VEpvcmk3bVgFH1Y9AVsmYWQQDEouTSggYXMFGStdKBEnCyQfBxo8Bw==»,»login-form[login]»:»»,»login-form[password]»:»»}
  [Page] http://localhost/index-test.php/user/login
  [Response] 200
  [Request Cookies] {«_csrf»:»dd395a9e5e3c08cfb1615dae5fc7b5ba0a2025c003e430ba0139b300f4a917ada:2:{i:0;s:5:»_csrf»;i:1;s:32:»QU9OhlK90Zc8GzEx59jkBjEIsAKmn-Q_»;}»}
  [Response Headers] {«content-type»:[«text/html; charset=UTF-8»]}
* I expect to see validations errors
* I see «Login cannot be blank.»
* I see «Password cannot be blank.»
* I am going to try to login with wrong credentials
* I fill field «input[name=»login-form[login]»]»,»admin»
* I fill field «input[name=»login-form[password]»]»,»wrong»
* I click «button[type=submit]»
  [Uri] http://localhost/index-test.php/user/login
  [Method] POST
  [Parameters] {«_csrf»:»QjFBRl9hMjMTZHgJNw15CnJrIn4YG3dLdwgrLR0Ld3oxcAorMUxjbA==»,»login-form[login]»:»admin»,»login-form[password]»:»wrong»}
  [Page] http://localhost/index-test.php/user/login
  [Response] 200
  [Request Cookies] {«_csrf»:»dd395a9e5e3c08cfb1615dae5fc7b5ba0a2025c003e430ba0139b300f4a917ada:2:{i:0;s:5:»_csrf»;i:1;s:32:»QU9OhlK90Zc8GzEx59jkBjEIsAKmn-Q_»;}»}
  [Response Headers] {«content-type»:[«text/html; charset=UTF-8»]}
* I expect to see validations errors
* I see «Invalid login or password»
* I am going to try to login with correct credentials
* I fill field «input[name=»login-form[login]»]»,»admin»
* I fill field «input[name=»login-form[password]»]»,»admin11″
* I click «button[type=submit]»
  [Uri] http://localhost/index-test.php/user/login
  [Method] POST
  [Parameters] {«_csrf»:»bG8uMXdPYk49Ohd.HyMpd1w1TQkwNSc2WVZEWjUlJwcfLmVcGWIzEQ==»,»login-form[login]»:»admin»,»login-form[password]»:»admin11″}
  [Headers] {«location»:[«http://localhost/index-test.php»],»content-type»:[«text/html; charset=UTF-8»]}
  [Page] http://localhost/index-test.php/user/login
  [Response] 302
  [Request Cookies] {«_csrf»:»dd395a9e5e3c08cfb1615dae5fc7b5ba0a2025c003e430ba0139b300f4a917ada:2:{i:0;s:5:»_csrf»;i:1;s:32:»QU9OhlK90Zc8GzEx59jkBjEIsAKmn-Q_»;}»}
  [Response Headers] {«location»:[«http://localhost/index-test.php»],»content-type»:[«text/html; charset=UTF-8»]}
  [Redirecting to] http://localhost/index-test.php
  [Page] http://localhost/index-test.php
  [Response] 200
  [Request Cookies] {«_csrf»:»dd395a9e5e3c08cfb1615dae5fc7b5ba0a2025c003e430ba0139b300f4a917ada:2:{i:0;s:5:»_csrf»;i:1;s:32:»QU9OhlK90Zc8GzEx59jkBjEIsAKmn-Q_»;}»}
  [Response Headers] {«content-type»:[«text/html; charset=UTF-8»]}
* I expect to see user info
* I see «Logout»
 PASSED

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

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

К сожалению, я не смог заставить модульные тесты работать в нашем дереве из-за небольших ошибок Yii, которые еще не были выпущены, или из-за проблем конфигурации между Codeception и пользователем yii2, которые мы интегрировали в Как программировать с Yii2: Интеграция регистрации пользователей .

1
2
3
4
5
6
Unit Tests (3) —————————————————————————————————-
Modules:
——————————————————————————————————————-
Trying to test login no user (tests\codeception\unit\models\LoginFormTest::testLoginNoUser)…
<pre>PHP Fatal Error &#039;yii\base\ErrorException&#039;
tests\codeception\unit\models\expect()&#039;

Я снова расскажу о модульном тестировании в нашей серии Startup, в которой не используется пользователь yii2, а вместо этого используется встроенная интеграция пользователей в дереве Yii Advanced.

Давайте посмотрим на пару примеров из дерева Yii2-app-basic.

Привет / tests / codeception / unit / models / ContactFormTest.php тестирует отправку электронной почты посредством программного использования моделей:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
 
namespace tests\codeception\unit\models;
 
use Yii;
use yii\codeception\TestCase;
use Codeception\Specify;
 
class ContactFormTest extends TestCase
{
    use Specify;
 
    protected function setUp()
    {
        parent::setUp();
        Yii::$app->mailer->fileTransportCallback = function ($mailer, $message) {
            return ‘testing_message.eml’;
        };
    }
 
    protected function tearDown()
    {
        unlink($this->getMessageFile());
        parent::tearDown();
    }
 
    public function testContact()
    {
        $model = $this->createMock(‘app\models\ContactForm’, [‘validate’]);
        $model->expects($this->once())->method(‘validate’)->will($this->returnValue(true));
 
        $model->attributes = [
            ‘name’ => ‘Tester’,
            ’email’ => ‘[email protected]’,
            ‘subject’ => ‘very important letter subject’,
            ‘body’ => ‘body of current message’,
        ];
 
        $model->contact(‘[email protected]’);
         
        $this->specify(’email should be send’, function () {
            expect(’email file should exist’, file_exists($this->getMessageFile()))->true();
        });
 
        $this->specify(‘message should contain correct data’, function () use ($model) {
            $emailMessage = file_get_contents($this->getMessageFile());
 
            expect(’email should contain user name’, $emailMessage)->contains($model->name);
            expect(’email should contain sender email’, $emailMessage)->contains($model->email);
            expect(’email should contain subject’, $emailMessage)->contains($model->subject);
            expect(’email should contain body’, $emailMessage)->contains($model->body);
        });
    }
 
    private function getMessageFile()
    {
        return Yii::getAlias(Yii::$app->mailer->fileTransportPath) .
    }
 
}

Мне не удалось успешно пройти этот тест из-за небольшой ошибки в Yii, которая еще не была обновлена ​​(или, по крайней мере, я не смог найти обновленный код ). Моя капля кодовой базы Yii называла исходящее электронное письмо с отметками даты, а код выше искал фиксированное имя файла. Таким образом, это всегда не удавалось. Тем не менее, полезно посмотреть, как программное тестирование может использовать модели для создания файла, а затем искать этот файл и проверять его содержимое, чтобы убедиться, что код работает.

Давайте посмотрим на hello / tests / codeception / unit / models / LoginFormTest.php. Опять же, мое использование yii2-пользователя сделало его слишком сложным для интеграции во время написания этого урока; однако мы можем взглянуть на концептуальный подход к модульному тестированию функций модели пользователя.

Вот testLoginCorrect() , который testLoginCorrect() , успешно ли выполнен вход в систему с правильным паролем:

01
02
03
04
05
06
07
08
09
10
11
12
13
public function testLoginCorrect()
   {
       $model = new LoginForm([
           ‘username’ => ‘admin’,
           ‘password’ => ‘admin11’,
       ]);
 
       $this->specify(‘user should be able to login with correct credentials’, function () use ($model) {
           expect(‘model should login user’, $model->login())->true();
           expect(‘error message should not be set’, $model->errors)->hasntKey(‘password’);
           expect(‘user should be logged in’, Yii::$app->user->isGuest)->false();
       });
   }

Он использует модель LoginForm для программного входа пользователя в систему, а затем программно LoginForm не является ли текущий пользователь Yii гостем.

1
expect(‘user should be logged in’, Yii::$app->user->isGuest)->false();

Я надеюсь, что вам понравилось узнавать о Codeception и его интеграции с Yii, несмотря на некоторые препятствия, с которыми я столкнулся. Установка по умолчанию yii2-basic сегодня должна работать лучше.

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

Следите за будущими уроками в нашей серии «Программирование с Yii2», поскольку мы продолжаем изучать различные аспекты фреймворка. Если вы хотите узнать, когда появится следующий учебник по Yii2, следуйте за мной @reifman в Твиттере или зайдите на страницу моего инструктора .

Вы также можете ознакомиться с нашей серией «Построение стартапа с помощью PHP» , в которой используется расширенный шаблон Yii2 при создании реального приложения. На самом деле, вы можете попробовать приложение для запуска, Meeting Planner , сегодня.