Edited at
CakePHPDay 9

CakePHP3 のアプリケーションを Behat でテストする(update編)

More than 1 year has passed since last update.

CakePHP Advent Calendar 2017の9日目です(ギリ間に合った...

@o0h さんの昨日の記事は、CakePHP3のコントローラーのテストはどうするかでした。

CakePHP3でBDDをするには、 Behat を使うことでシナリオテストを記述することができます。

BehatはE2Eテストをするのに適したテスティングフレームワークで、自然言語(つまり日本語)で記述できるのが特徴です。

CakePHP2でBDDをしたい方は、私作ではありますが CakePHP2 BDDプラグインがありますので、ご利用ください。


きっかけ

実は最近BehatとかBDDとか言ってなかったのですが、このツイートが飛んできました。

うわー、2年以上もメンテしてなかったCakePHP3のBDDチュートリアルです。

そしてインターネット怖いね、CakePHP + BehatについてCakeFestで発表した動画 - 2014年まであるよ。

これ、最新版のPHPとかCakePHPでも動くのかな....

と心配になったのは言うまでもありません。


2年前の記憶

こういうBlogを日本語でも書いていました。

CakePHP3 のアプリケーションを Behat でテストする

今回は、このブログ記事で書いていた内容を、現時点のバージョンにアップグレードしてみたよ編としてお送ります。


アップデートしてみた


PHP7.1 へのアップグレード


  • Vagrant/Virtual Boxを最新版にする

  • まっさらな状態から手順を進めてみる

  • cakeboxのサイトにPHP 7.1へのアップグレード手順があったのでやってみる

さて、ここでPHP7.1 のアップグレードで失敗します。

どうもアップグレードシェルでPHP7をインストールするのにPPAリポジトリを追加するため add-apt-repository コマンドを使っているのですが、見つからないとエラーになります。

これを予めインストールするように README.mdに追記しました。

localhost:cakebox $ vagrant ssh

vagrant@cakebox $ sudo apt-get update
vagrant@cakebox $ sudo apt-get install software-properties-common python-software-properties
vagrant@cakebox $ /cakebox/bash/ubuntu-16.sh
vagrant@cakebox $ exit
localhost:cakebox $ vagrant reload

アップグレードが終わるとPHPのバージョンが7.1.12になっています。

localhost:cakebox $ vagrant ssh

vagrant@cakebox:~$ php -v
PHP 7.1.12-2+ubuntu16.04.1+deb.sury.org+2 (cli) (built: Dec 7 2017 20:12:04) ( NTS )


依存関係のアップグレード

いったん以下の手順まで進めます。



vagrant@cakebox:~/Apps/blog-tutorial.app$ composer install

これで composer.lock ファイルから2年前のバージョンがインストールされます。

composer.json をみると、そこそこ書き換わってくれそうですので、 composer update で全アップデートをかけます(update された部分だけ抜粋しています)。

vagrant@cakebox:~/Apps/blog-tutorial.app$ composer update

Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 24 installs, 53 updates, 6 removals
- Updating cakephp/plugin-installer (0.0.11 => 1.0.0): Downloading (100%)
- Updating mobiledetect/mobiledetectlib (2.8.12 => 2.8.28): Downloading (100%)
- Updating psr/log (1.0.0 => 1.0.2): Downloading (100%)
- Updating cakephp/cakephp (3.0.1 => 3.4.1): Downloading (100%)
- Updating robmorgan/phinx (v0.4.3 => v0.8.1): Downloading (100%)
- Updating cakephp/migrations (1.1.0 => 1.7.1): Downloading (100%)
- Updating vlucas/phpdotenv (v1.1.0 => v1.1.1): Downloading (100%)
- Updating jakub-onderka/php-console-highlighter (v0.3.1 => v0.3.2): Downloading (100%)
- Updating nikic/php-parser (v1.2.2 => v3.1.2): Downloading (100%)
- Updating psy/psysh (v0.4.4 => v0.8.15): Downloading (100%)
- Updating cakephp/debug_kit (3.0.1 => 3.11.2): Downloading (100%)
- Updating cakephp/bake (1.0.4 => 1.3.7): Downloading (100%)
- Updating behat/mink (v1.6.1 => v1.7.1): Downloading (100%)
- Updating behat/transliterator (v1.0.1 => v1.2.0): Downloading (100%)
- Updating behat/gherkin (v4.3.0 => v4.5.1): Downloading (100%)
- Updating behat/behat (v3.0.15 => v3.4.3): Downloading (100%)
- Updating behat/mink-extension (v2.0.1 => 2.3.0): Downloading (100%)
- Updating guzzlehttp/guzzle (5.2.0 => 6.3.0): Downloading (100%)
- Updating fabpot/goutte (v2.0.3 => v3.2.2): Downloading (100%)
- Updating behat/mink-browserkit-driver (v1.2.0 => v1.3.2): Downloading (100%)
- Updating behat/mink-goutte-driver (v1.1.0 => v1.2.1): Downloading (100%)
- Updating sebastian/version (1.0.5 => 1.0.6): Downloading (100%)
- Updating sebastian/global-state (1.0.0 => 1.1.1): Downloading (100%)
- Updating sebastian/recursion-context (1.0.0 => 1.0.5): Downloading (100%)
- Updating sebastian/exporter (1.2.0 => 1.2.2): Downloading (100%)
- Updating sebastian/environment (1.2.2 => 1.3.8): Downloading (100%)
- Updating sebastian/diff (1.3.0 => 1.4.3): Downloading (100%)
- Updating sebastian/comparator (1.1.1 => 1.2.4): Downloading (100%)
- Updating phpunit/php-text-template (1.2.0 => 1.2.1): Downloading (100%)
- Updating doctrine/instantiator (1.0.4 => 1.1.0): Downloading (100%)
- Updating phpunit/phpunit-mock-objects (2.3.1 => 2.3.8): Downloading (100%)
- Updating phpunit/php-timer (1.0.5 => 1.0.9): Downloading (100%)
- Updating phpunit/php-file-iterator (1.4.0 => 1.4.5): Downloading (100%)
- Updating phpunit/php-token-stream (1.4.1 => 1.4.12): Downloading (100%)
- Updating phpunit/php-code-coverage (2.0.16 => 2.2.4): Downloading (100%)
- Updating phpdocumentor/reflection-docblock (2.0.4 => 4.2.0): Downloading (100%)
- Updating phpspec/prophecy (v1.4.1 => 1.7.3): Downloading (100%)
- Updating phpunit/phpunit (dev-master 13a957b => 4.8.36): Checking out 46023de9a9
- Updating fzaninotto/faker (v1.4.0 => v1.7.1): Downloading (100%)
- Updating sizuhiko/fabricate (2.0.0 => 2.0.3): Downloading (100%)
- Updating sizuhiko/cake_fabricate dev-master (1b445ef => ed1f8d4): Checking out ed1f8d4751
symfony/var-dumper suggests installing ext-symfony_debug ()
Writing lock file
Generating autoload files

いろいろ新しくなりました。

あとは元の手順を進め(PHPのバージョンが変わった部分を適時置き換え)、緊張の瞬間です。


Behatのテストを実行する

vagrant@cakebox:~/Apps/blog-tutorial.app$ vendor/bin/behat

Feature:
In order to tell the masses what's on my mind
As a user
I want to read articles on the site

Background: # features/articles.feature:7
Given there is a post: # ArticlesContext::thereIsAPost()
| Title | Body |
| The title | This is the post body. |
| A title once again | And the post body follows. |
| Title strikes back | This is really exciting! Not. |
And there is a user: # UsersContext::thereIsAUser()
| Username | Password | FirstName | LastName |
| alice | ecila | Alice | Smith |
| bob | obo | Bob | Johnson |
And there is a category: # CategoriesContext::thereIsACategory()
| Name |
| Events |
| Computers |
| Foods |

Scenario: Show articles # features/articles.feature:23
When I am on "TopPage" # WebContext::visit()
Then I should see "The title" # WebContext::assertPageContainsText()
And I should see "A title once again" # WebContext::assertPageContainsText()
And I should see "Title strikes back" # WebContext::assertPageContainsText()

Scenario: Show the article # features/articles.feature:29
Given I am on "TopPage" # WebContext::visit()
When I follow "A title once again" # WebContext::clickLink()
Then I should see "And the post body follows." # WebContext::assertPageContainsText()

Scenario: Add new article # features/articles.feature:34
Given I am on "TopPage" # WebContext::visit()
And I follow "Add" # WebContext::clickLink()
And I login "bob" "obo" # UsersContext::iLogin()
When I post article form : # ArticlesContext::iPostArticleForm()
| Label | Value |
| Categories | Events |
| Title | Today is Party |
| Body | From 19:30 with Alice |
And I should see "Your article has been saved." # WebContext::assertPageContainsText()
And I should see "Today is party" # WebContext::assertPageContainsText()

Scenario: Remove article # features/articles.feature:46
Given I am on "TopPage" # WebContext::visit()
When I delete article "Title strikes back" # ArticlesContext::iDeleteArticle()
Then I should not see "Title strikes back" # WebContext::assertPageNotContainsText()

4 scenarios (4 passed)
28 steps (28 passed)
0m12.62s (32.10Mb)

おー、全部成功しています。


何が言いたかったのか

さて、皆さんは現場で composer update なんて実行しますか?

まぁ普通はしませんよね。

私は今回やりました。もちろんサンプルアプリだし、仕事のアプリじゃないから、というのも言えるでしょう。

とはいえ、サンプルアプリでも動作確認をしないといけませんし、うまく動かなかったら...

このサンプルアプリには単体テストはありませんが、E2Eテストは存在していました(まぁE2Eのチュートリアルなので当然ですが...)。

全部アップデートしても、エンドツーエンドでちゃんと動くことがコマンド1つで確認できます。

単体テストももちろん大事ですが、E2Eテストがあるときの安心感は今回改めて感じました。


さいごに

あいかわらず単体テスト(PHPSpec)に関しては未着手なのですが、いろいろ考えてPHPUnitベースでBDDスタイルに記述できる Codeception/Specify を使うと良いのかなーと思います。これだとPHPUnitのコードと共存できるし、少しずつ書き換えたりできるな、と。

CakePHP3のBDDチュートリアルは、最新版で試せるように変更は反映されています。

明日は @ryoichi-u さんの「Phinxの話か、Formヘルパーのエスケープ周りか」が予定されています。楽しみですね。