atijust's blog

技術的なこととか。

Laravel + Codeception2 でブラウザテストを自動化する

この記事はLaravel Advent Calendar 2014 22日目です。今日はCodeceptionを使ったLaravelアプリケーションのブラウザテストの自動化を紹介したいと思います。

はじめに

Webアプリケーションの開発現場でよくある光景として、ブラウザを使って人力でポチポチやって動作確認をしたりしますよね。ユニットテストを書いているといっても、Webサーバやデータベースまで含めた実際に動いているシステム全体の結合テストとして、リリース前にはブラウザでの動作確認はできればやっておきたいもの。ただ、一度や二度ならまだしもリリースの度に何度も何度も人力でテストするのは大変です(主に精神的に)。リリース頻度が日に数度といったレベルの場合は、そもそも人力テスト自体が不可能になるということも。もうこれは自動化するしかないですね。ということでCodeceptionです。

Codeceptionはユニットテスト、機能テスト、受け入れテストをサポートしたフルスタックPHP用テスティングフレームワークです。いわゆるBDDフレームワークといわれるもので(厳密にいうとどうなのかはわかりませんが……)、自然言語(英語)に近い記法でテスト対象の振る舞いを記述しテストを行うことができます。

Codeceptionがサポートする3種類のテストのなかでブラウザによるテストに相当するのが受け入れテスト(Acceptance Test)です。受け入れテストとは、アプリケーションの利用者であるユーザの視点から、アプリケーションが期待したとおりに動作するか確認するテストのことです。サイトトップにアクセスして、ナビバーからログインページに飛んで、ユーザ名とパスワードを入力して、ログインボタンをクリックして、Welcome!と表示されたらOK……こういったユーザからみたアプリケーションの挙動を記述したシナリオを元に、Guzzle+Symfony BrowserKitによるスクレイパー(CodeceptionではPhpBrowserと呼ばれています)やPhantomJSSelenium WebDriverで実際に動作中のアプリケーションにアクセスして挙動をテストすることができます。

この記事ではLaravel Tricksをサンプルとして、Codeceptionで受け入れテストを記述する方法を紹介します。受け入れテストの実行にはPhpBrowserを使用します。PhpBrowserはJavaScriptの実行はできませんが、Codeceptionに標準で入っているのでお手軽です。JavaScriptが必要な場合はPhantomJSやSelenium WebDriverを使用する必要がありますが、これらは別途インストールが必要です。詳しくはCodeceptionのマニュアルを参照してください。

Laravel Tricksのセットアップ

受け入れテストを行うにはWebサーバ、データベース含めてアプリケーションが動作している必要があります。下準備としてまずはLaravel Tricksをローカル環境で動くようにしておきます。

Laravel Tricksのセットアップ自体は本題ではないので、ここではわたしの開発マシンにあわせてあらかじめ設定を済ませておいたforkを利用します*1。Laravel Tricksのセットアップについて詳しくはLaravel Tricksのドキュメントを参照ください。

$ git clone https://github.com/atijust/laravel-tricks.git
$ composer install
$ php artisan migrate
$ php artisan db:seed
$ php artisan serve

PHPの組み込みサーバが起動し、http://localhost:8000/でLaravel Tricksにアクセスできるようになります。このローカルホストで動いているLaravel Tricksにたいして受け入れテストを実行します。

Codeceptionのインストール

composerでcodeception/codeceptionをインストールします。

$ composer require "codeception/codeception:2.*"

テストに必要な各種ファイルが格納されたtestsディレクトリと、グローバル設定ファイルであるcodeception.ymlを生成します。

$ php vendor/bin/codecept bootstrap
Initializing Codeception in /Users/atijust/Develop/laravel-tricks

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
\AcceptanceTester includes modules: PhpBrowser, AcceptanceHelper
AcceptanceTester.php generated successfully. 48 methods added
\FunctionalTester includes modules: Filesystem, FunctionalHelper
FunctionalTester.php generated successfully. 13 methods added
\UnitTester includes modules: Asserts, UnitHelper
UnitTester.php generated successfully. 17 methods added

Bootstrap is done. Check out /Users/atijust/Develop/laravel-tricks/tests directory

以上でCodeceptionのインストールは完了です。

シナリオを作成

例としてログインのシナリオを作成してみましょう。

Laravel Tricksのログインの流れは次のようになります。

  1. /loginにアクセス
  2. ユーザ名とパスワードを入力
  3. LOGINボタンをクリック
  4. 投稿したトリックの一覧が表示されればログイン成功

これをシナリオに書き下してみます。

まずはシナリオを生成します。

$ php vendor/bin/codecept generate:cept acceptance Signin
Test was created in SigninCept.php

tests/acceptance/SigninCept.phpにひな形が生成されているのでシナリオを記述します。

<?php
$I = new AcceptanceTester($scenario);
$I->wantTo('log in as regular user');
$I->amOnPage('/login');
$I->fillField('#username','msurguy');
$I->fillField('#password','password');
$I->click('#wrap > div.container > div > div > div > form > div:nth-child(5) > button');
$I->see('My tricks');

なにをやっているかはだいたいわかるのではないでしょうか。

テストシナリオは自然言語(英語)に近いかたちで記述することができます。とはいってもDSLというわけではなく普通のPHPコードです。あくまでもそれっぽく記述できるというだけです*2

まず$I->amOnPage('/login');でログインページにアクセスします。

次に$I->fillField('#username','msurguy');でユーザ名、$I->fillField('#password','password');でパスワードをフォーム入力します。ユーザ名とパスワードはシーダーでデフォルトで用意されているものです。入力するフィールドはCSSパスで指定していますが他にもさまざまな指定方法が利用できます。

そして$I->click('#wrap > div.container > div > div > div > form > div:nth-child(5) > button');でLOGINボタンをクリック。ここでもCSSパスでLOGINボタンを指定しています。

ログインが成功したら投稿したトリックの一覧が表示されるのですが、その際に「My Tricks」という文字列が画面に出ているはずなので、それがあればログイン成功とみなします。

テストの実行

テストを実行してみましょう。

アプリケーションのURLをtests/acceptance.suite.ymlに設定します。

    config:
        PhpBrowser:
            url: 'http://localhost:8000/'

テストを実行します。

$ php vendor/bin/codecept run acceptance --steps
Codeception PHP Testing Framework v2.0.9
Powered by PHPUnit 4.4.0 by Sebastian Bergmann.

Acceptance Tests (1) -----------------------------------------------------------------------------------------------
Trying to log in as regular user (SigninCept)
Scenario:
* I am on page "/login"
* I fill field "#username","msurguy"
* I fill field "#password","password"
* I click "#wrap > div.container > div > div > div > form > div:nth-child(5) > button"
* I see "My tricks"
 PASSED

--------------------------------------------------------------------------------------------------------------------


Time: 715 ms, Memory: 13.25Mb

OK (1 test, 1 assertion)

成功したようです。

このようにしてCodeceptionを使えばブラウザを使ったテストを自動化することができます。ここでは非常な簡単な例のみで、使用したCodeceptionの機能も基礎的なものだけでしたが、受け入れテストを記述するための豊富な機能がCodeceptionには用意されています。スクショ撮ったりとかもできます。

以上、Laravel Advent Calendar 2014 22日目でした。

*1:オリジナルのLaravel TricksはPHPUnitのバージョンが低かったり、一部のCSSや画像ファイルのパス指定がおかしかったりするので、設定以外にそれらも修正しています。

*2:この辺の設計思想については公式ブログで解説させているので興味があれば参照ください。