テストの記述と実行
準備ができたので、テストの記述と実行に移ります。テストは、PHPUnitのスクリプトを呼び出して実行します。順番に、PHPUnitの設定ファイルを作成する方法、テストの記述方法、テストを実行するための手順と出力の結果の見方について説明します。
環境の設定
テストの環境に関する設定を行います。まずは、テスト全体に関する設定を行うphpunit.xmlです。
<phpunit bootstrap="./application/bootstrap.php" colors="true"><!-- (1) --> <testsuite name="CodezineProject"><!-- (2) --> <directory>./application</directory><!-- (3) --> </testsuite> </phpunit>
名前のとおり、phpunit.xmlはPHPUnit関係の設定ファイルで、どのテストをどのように実行するかを記述できます。ここでは単純にテストを行う環境と、テストに関する設定を行っています。
(1)のphpunit要素はこの中でテストに関する記述を行うことの宣言です。ここでは2つの属性「bootstrap」と「colors」を設定しています。bootstrap属性ではスクリプト名を指定しています(後述)。colors属性の値はtrueになっていますが、これはテスト結果を表示する際にカラーで出力することを指定しています。phpunit要素の内部には、それぞれのテストについて記述していきます。今回記述するテストは1つだけで、(2)にあるname属性がCodezineProjectであるテストだけです。
テストについては(2)のようにtestsuite要素での内側に記述します。例えばテストが実際に書かれているファイルが格納されているディレクトリを指定するには(3)のようにdirectory要素を利用します。ここでは利用していませんが、ディレクトリ単位でなく、ファイル単位で指定するためのfile要素も存在します。
なお、この設定ファイルを利用することでテストに関するさまざまな設定を行うことができます。詳細についてはPHPUnitのマニュアルなどを参照ください。
リスト6(1)のbootsrtap属性で指定したbootsrtap.phpは、テストを行う環境を記述します。通常のMVCアプリケーションの場合にはindex.php内で定数の設定が行われた上でZend_Applicationオブジェクトの作成、ブートストラップ、処理のディスパッチが行われます。bootstrap.phpでは、これらの処理のうち、定数の設定に対応する部分を行います。
<?php /* ファイルのある場所を指定する定数を定義 */ define('BASE_PATH', realpath(dirname(__FILE__) . '/../../')); define('APPLICATION_PATH', BASE_PATH . '/application'); /* PHPファイルを探す場所の追加 */ set_include_path( '.' . PATH_SEPARATOR . BASE_PATH . '/library' . PATH_SEPARATOR . get_include_path() ); /* 実行環境を指定する定数を定義 */ define('APPLICATION_ENV', 'testing');
ここで書かれている内容は標準のindex.phpとほぼ同じで、アプリケーション本体がどこにあるか、実行を行っている環境が何であるかなどを定数に設定しています。ここにある定数で、アプリケーション関係のファイルがどこにあるかを指定したり(APPLICATION_PATH)、アプリケーションの開発の段階を指定したりします(APPLICATION_ENV)。
ここまでで、テストを書くための準備ができました。
テストの作成と実行
次に、IndexControllerTest.phpにテストに関する記述を行います。テスト環境のうち、定数等に関しては先程のリスト7で設定したので、それ以外の部分について記述していきます。
<?php require_once 'Zend/Application.php'; require_once 'Zend/Test/PHPUnit/ControllerTestCase.php'; class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase//(1) { public function setUp()//(2) { /* Setup Routine */ //(2-1) $this->application = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini' ); //(2-2) $this->bootstrap = array($this, 'appBootstrap'); //(2-3) parent::setUp(); } public function appBootstrap()//(3) { $this->application->bootstrap(); } public function tearDown()//(4) { /* Tear Down Routine */ } ... }
setUpメソッド、tearDownメソッドはもともと定義されてたメソッドも残っていますが、それ以外の部分は継承しているクラスからして、ほぼ全部を書き換えています。
まず(1)ですが、PHPUnit_Framework_TestCaseクラスを継承していたIndexControllerTestクラスの親クラスをZend_Test_PHPUnit_ControllerTestCaseクラスに変更し、Zend_Testコンポーネントの機能を利用できるようにします。ここが重要で、元のままではPHPUnitの機能しか使えませんが、継承するクラスを変えることでZend_Testコンポーネントの機能が使えるようになります。
なお、前にも述べましたがZend_Test_PHPUnit_ControllerTestCaseクラスはPHPUnit_Framework_TestCaseクラスも継承しているため、PHPUnitの機能もそのまま利用できます。
テストを行う前の設定とテスト終了後の後処理はそれぞれ(2)setUpメソッドと(4)tearDownメソッドに記述します。このsetUpメソッドでは(2-1)Zend_Applicationオブジェクトの作成と(2-2)ブートストラップ関数の登録を行った後、(2-3)親クラスであるZend_Test_PHPUnit_ControlleTestCaseのコンストラクタ(実際にはPHPUnit_Framework_TestCaseのコンストラクタ)を呼び出しています。また、ここでは利用していませんが、(4)の後処理メソッドでは、例えばテストに使った一時ファイルを削除する等の処理を記述します。
この(2-2)で登録されているブートストラップ関数の本体は(3)で、ここでは単純に(2-1)で作成したZend_Applicationオブジェクトのbootstrapメソッドを呼び出しています。
実際のテストは、この後にtestで開始する名前のメソッドを追加することで登録します。ここで、対象とするアプリケーションは次の条件を満たしているとします。
- Webサイトのトップページにアクセスした場合にはユーザ名とパスワードを入力するためのフォームを表示する
- 正しいユーザ名とパスワードが入力された場合にはcorrect、そうでない場合にはwrongと表示する
これをチェックするためのテストを、今回は3つのメソッドで表現します。
class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase { ... /*(1)indexアクションのテスト */ public function testIndexAction() { $this->dispatch('/'); $this->assertController('index'); $this->assertAction('index'); $this->assertQueryCount('form#loginForm', 1); } /*(2)パスワードを間違えた場合 */ public function testWrongPass() { $this->request->setMethod('POST') ->setPost(array( 'username' => 'badperson', 'password' => 'dontknow' )); $this->dispatch('index/login'); $this->assertQueryContentContains('p', 'wrong'); } /*(3)パスワードが正しい場合 */ public function testCorrectPass() { $this->request->setMethod('POST') ->setPost(array( 'username' => 'codezine', 'password' => 'php' )); $this->dispatch('index/login'); $this->assertQueryContentContains('p', 'correct'); } }
追加したメソッドはそれぞれ(1)トップページへのアクセスのテスト(2)パスワードを間違えた場合のテスト(3)パスワードが正しかった場合のテストです。dispatchメソッドでは、テストでどのURLにディスパッチするのかを指定します。
$this->dispatch($url);
また、requestプロパティではティスパッチの際のリクエストオプジェクトにアクセスできます。
$this->request
このプロパティを利用することで、POSTを含むリクエストを使う場合の設定ができます。条件記述のためのメソッドは次のものを利用しています。
メソッド名 | 引数 | 説明 |
assertController | $controller | ディスパッチされたアクションコントローラが$controllerであると仮定 |
assertQueryCount | $path, $count | 出力に$pathで示された要素が$count個あると仮定 |
assertQueryContentContains | $path, $match | 出力の$pathで示された要素が$matchにマッチすると仮定 |
ここで記述した条件は仮定が成り立てばテストは成功、成り立たない場合にはテストが失敗となります。例えば(1)の「$this->assertQueryCount('form#loginForm', 1);」は、出力にloginFormというidのformが1つだけあった場合にのみ成功という意味になります。また、(2)の「$this->assertQueryContentContains('p', 'wrong');」は、出力のp要素の内側にwrongという文字列があるかという意味です。なお、各メソッドについては次回、より詳しく取り上げる予定です。
テストを実行するにはphpunitを利用します。すでにphpunit.xmlにどのようなテストを実行するかのは記述してあるので、引数なしで呼び出します。
>php /Program\ Files/PHP/phpunit
出力は以下のようになります。
今回は3つテストがあって、3つとも失敗しています。そのため、最初にテストが3つ失敗したことが表示され、その後に順番にどのテストが何処で失敗したかが表示されています。
また、図1のキャプチャはカラー表示ができる端末を利用していますが、このようにカラー表示可能な端末を利用した上でリスト6の(1)のようにカラーを有効にすると、テストに失敗した場合には報告の最後が赤く表示されます。
実装と再テスト
テストが実装できたので、次にテストが通るように実装を行います。ここではIndexController.php、index.phtmlに実装を行います(詳細については過去の記事やサンプルコードを参照ください)。
ここでテストに成功すると、以下の画面のような画面が表示されます。
前回テストに失敗したときには大量のエラーメッセージ表示されていましたが、テストに成功した場合にはこのように簡潔な表示になります。
おわりに
今回はZend FrameworkのMVC環境向けの単体テストフレームワークであるZend_Testコンポーネントの紹介を行いました。Zend_Testコンポーネントを利用するための環境設定から、簡単なテストの記述とテストの実行方法まで紹介しました。
次回は、テストにどのような条件を記述できるかについて、より詳細に解説しようと思います。