CakePHP3のPHPUnitをブラウザからテストする方法

CakePHP2 まで PHPUnit の Webrunner (webroot/test.php) が公式で提供されていましたが、CakePHP3 からはこれが削除されてしまいました。

CakePHP 3.x Migration Guide によると、「ユニットテストをしたい人は CLI による PHPUnit をインストールしてね。更にブラウザからテストしたい人は VisualPHPUnit (Webrunner) を別途インストールしてね。」という方針のようです。

便利だったのに残念ですね(´・_・`)

VisualPHPUnit とは

PHPUnit をウェブブラウザから実行してくれる外部ツールです。これまでの test.php で出来ていたことは大体出来ると思います。

UI はこのような感じで、なかなかお洒落。

VisualPHPUnit のスクリーンショット

インストールにあたっての前提

この記事のインストール方法は PHPUnit がインストール済みで、CLI から UnitTest が行える状態になっていることが前提です。

もしまだの場合、公式ドキュメントの Installing PHPUnit の項を参照してみてください。

CakePHP3.x に VisualPHPUnit をインストールする手順

今のところ公式 Cookbook にはインストール方法が載っていない為、独自の方法になります。

基本には VisualPHPUnit の README の説明の通りで、VisualPHPUnit を公開ディレクトリに入れて設定してアクセスするだけです。(本当は composer でインストールしたい..)

1. VisualPHPUnit をダウンロードする

VisualPHPUnit のリポジトリから Download ZIP してきます。git clone でも大丈夫だと思います。

2. webroot に VisualPHPUnit を入れる

ZIP 展開後のディレクトリを test にリネームして webroot/test に配置します。

3. キャッシュディレクトリの書き込み権限を設定する

webroot/test/app/resource/cache ディレクトリの権限を 777 にします。

chmod 777 -R webroot/test/app/resource/cache

4. 設定ファイルの設定を行う

設定ファイルの webroot/test/app/config/bootstrap.php を開いて以下の3箇所を書き換えます。

composer_vendor_path のパス

// 'composer_vendor_path' => $root . '/vendor',
'composer_vendor_path' => $root . '/../../vendor',

test_directories のパス

    'test_directories' => array(
        //'Sample Tests' => "{$root}/app/test",
        //'My Project' => '/var/www/sites/my.awesome.site.com/laravel/tests',
        'App' => "{$root}/../../tests/TestCase",
    ),

bootstraps のパス

    'bootstraps' => array(
        // '/path/to/bootstrap.php',
        //'/var/www/sites/my.awesome.site.com/laravel/bootstrap/autoload.php',
        "{$root}/../../tests/bootstrap.php",
    )

5. 必要に応じてウェブサーバの設定やその他設定を行う

Apache や nginx の追加設定が必要な場合があります。VisualPHPUnit の README を参照してみてください。

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

使い方

ブラウザから webroot/test ディレクトリにアクセスし、左側の FILES 欄でテストしたいテストケースを選択し「Run Tests」をクリックするとテスト結果が表示されます。

URL例: http://example.com/test/

ちなみに TestCase 上で echo や var_dump() をすると結果画面上にデバッグ用として出力してくれるので便利です。

2016/02/22更新: 設定の test_directories のパス定義に /TestCase を追加しました。

フレームワーク開発はジグソーパズルと似ている

こんにちは。ウェブ系システム開発者の山本です。

ウェブ開発のフレームワークって最近は本当にたくさんありますね。メジャーなところだとCakePHPや、Symfonyzend frameworkCodeIgniter、あとはRubyでお馴染みruby on railsなどでしょうか。

ここしばらくPHPフレームワークでの開発を継続的にやっています。去年はCakePHP1とCakePHP2を、そして今年はSymfonyを使った実務開発をしています。楽しくコーディングの日々を続ける中で、ジグソーパズルとフレームワークを用いた開発ってとても似ているなってふと思いました。

とにかく手を動かして答えを探る

パソコンでカタカタプログラムを打ち込むフレームワーク開発と、ジグソーパズル。
この2つの一体何が似ているのか。

直感的に感じた共通点は手を動かすことです。

どちらも、とにかく手を動かし探って作り上げていきます。ピースを組み立てていくと徐々に絵が見えてきます。

ピースをはめるまでのもやもやした感じ、
そしてピースがカチッとハマった時の心が小さく踊る感じ。

フレームワークに用意されている機能や仕組みはパズルのピース

必要なピースは、基本的にフレームワーク上に全て用意されています。

用意された無数の部品が正にピースのようなもので、ピッタリとピースをはめるまでの過程が苦労します。ですが、ぴったりとはまる瞬間が気持ちいいし楽しいです。

そしてピースがはまる度に徐々にリズムが生まれてきます。ジグソーパズルも後半になるほど楽ですね。

検索フォームを作った時に強く実感

パズルな感覚はデータ検索フォームを作った時に強く実感しました。

フォームに入力した文字でデータベースを検索して表示するシンプルな画面。
なにもフレームワークの仕組みを理解してまで頑張って検索しなくても、自分でゴリゴリ書いて実現してしまえばいいじゃんと思いながらも、フレームワークの仕組みを使って頑張って実装することにしました。

ピースが合うまでが、ものすごーく苦労しました!

そもそも、どうプログラムを書いていいのかわからないですし、
フレームワークで一体なにが行われているのか分からない!

思い通りにシステムが動かない時間が続きました。

・・・正直もうあまりやりたくない作業です。

開発を楽にするためのフレームワークなのに、
こんなに苦労して意味があるの?ってちょっと思いました。

でもでも!

それでも、マニュアルや動作サンプルを調べて動かしてみて、やっと動いた時に真価が分かりました。間違いなく苦労した以上の価値がありました。

動くまで苦労するけど、動いてしまえば「あれ、もう全部出来てる」って。データを検索できるし、表示もできる、入力チェックもできる、更にページングの実装まで出来上がってる…!

自力でこれらをコーディングする大変さも知っている自分にとっては、これがフレームワークの力なのかと唖然としましたね。

フレームワークに用意された仕組みを使って実現すること

せっかくフレームワークを使っているのですから、
是非フレームワークの仕組みを使ってやりたい事を実現することを検討してみてください。

必ずしもそれが最良とは限らないですし、時と場合によりますが
動いてしまえば苦労した以上の利益が得られることも多くあります。

新たなフレームワークやプラグインの使い方を知ることで、
今後の開発が圧倒的に楽になります。

フレームワークの理解を深めることは今後への投資とも言えるでしょう。

Symfonyで2つのフォーム値が同じかどうかを比較するバリデート

フォームで2つの入力値が同じ入力値か判定したい場面って結構あると思います。例えば確認用パスワードでの再入力や、メールアドレスの再入力など。

せっかくフレームワークを使っているので、SymfonyのsfFormで利用できるバリデート機能でチェックしたほうが良いと思います。

sfValidatorSchemaCompareクラスの利用

今回はsfValidatorSchemaCompareというクラスを用います。こちらのバリデーションクラスでは2つのフィールドにまたがって値の比較を柔軟に行えます。例えば入力項目Aより入力項目Bの値のほうが小さいかなどというチェックもできます。

サンプルコードと解説

class RegisterForm extends sfForm
{
    public function configure()
    {
        $this->setWidgets(
            array(
                'password' => new sfWidgetFormInputPassword(
                    array('label' => 'パスワード')
                ),
                'again_password' => new sfWidgetFormInputPassword(
                    array('label' => '確認用パスワード')
                )
            )
        );

        $this->setValidators(
            array(
                'password' => new sfValidatorString(
                    array('required' => true)
                ),
                'again_password' => new sfValidatorString(
                    array('required' => true)
                )
            )
        );
 
        $this->validatorSchema->setPostValidator(
            new sfValidatorSchemaCompare(
                'again_password',
                '==',
                'password',
                array('throw_global_error'=> false),
                array('invalid'=> '確認用パスワードが一致していません')
            )
        );
    }
}

passwordフィールドとagain_passwordフィールドが同一かどうかをチェックしています。
比較したい二つのフィールド名の引数の間に比較演算子となっており、普段の式のように直感的に書ける引数構成だと思います。

again_password == password

sfValidatorSchemaCompareコンストラクタの引数の仕様

symfony Forms in Action | 付録 B – バリデータ | symfony | Web PHP Framework

sfValidatorSchemaCompare($left_field, $operator, $right_field, $throw_global_error)

第二引数$operatorについての詳細について以下引用です。

利用可能な演算子の一覧は次のとおりです。

  • sfValidatorSchemaCompare::EQUAL もしくは ==
  • sfValidatorSchemaCompare::IDENTICAL もしくは ===
  • sfValidatorSchemaCompare::NOT_EQUAL もしくは !=
  • sfValidatorSchemaCompare::NOT_IDENTICAL もしくは !==
  • sfValidatorSchemaCompare::LESS_THAN もしくは <
  • sfValidatorSchemaCompare::LESS_THAN_EQUAL もしくは <=
  • sfValidatorSchemaCompare::GREATER_THAN もしくは >
  • sfValidatorSchemaCompare::GREATER_THAN_EQUAL もしくは >=

throw_global_error引数をtrueとするとGlobal errorとして投げられます。
falseとするとnamed errorが左側のフィールドにNamed errorが投げられます(デフォルトの挙動)

  • Named error – 単一フィールドに対するエラー
  • Global error – 複数フィールドにまたがる、フォーム共通のエラー

しばらくはSymfonyの開発実践をしながら勉強の日々が続きそうです。
さーがんばろう。