xUnitでは一般的にテスト対象のクラスをテストケース内でインスタンス化し、実際にメソッドをコールすることによってその結果を精査します。つまり、本来アプリケーションフレームワーク内などでの使用を想定しているクラスを、テストケースという仮想的な環境で実行するわけです。
当然、テストケース内はPHPUnitで提供された環境ですので通常のアプリケーションフレームワークが行う初期化処理や、データベース接続などの準備処理を、テストの前に実行しておく必要があります。
また、テストを実行することによって、そのマシン上に永続的なデータが残る場合があります。例えば、データベースへの値投入や、ファイルシステム上への書き込みなどがそうです。
ユニットテストは繰り返し実行しなければ意味がありません。テスト実行時に前回のデータや一時ファイルが残っていては、正しいテストが行えない場合がありますので、テストが終了した際にこれらを片付ける必要があります。
この準備と後始末の仕組みについてもPHPUnit側でテンプレートメソッドとして準備されています。親クラスから呼び出しのみ行われますので、子クラスであるテストケース内でメソッドを継承し、処理を実装しておけば、それぞれのタイミングで実行してくれます。メソッド名と実行のタイミングは以下の表のとおりです。
メソッド名 | 実行のタイミング |
---|---|
setUpBeforeClass() | テストケース開始時 |
setUp() | テストメソッド実行前 |
tearDown() | テストメソッド実行後 |
tearDownAfterClass() | テストケース終了時 |
setUpBeforeClass()とtearDownAfterClass()メソッドは1テストケースにつき1回しか実行されませんが、setUp()とtearDown()メソッドはそれぞれ実装されたテストメソッドの数だけ呼ばれるという点が違います。
それでは実際にテストケースを作成してみましょう。テスト駆動開発ではコードを書く前にテストを書くことになりますので、先にPHPUnitのテストケースを準備します。
今回は例として「社員データベーステーブルへのデータアクセスクラス」を開発します。Employeeクラスを作成し、保存:save()メソッドを実装。社員情報として、氏名:$name、年齢:$ageをプロパティとして持ち、それぞれのアクセサメソッドを用意します。
また、コンストラクタに引数でIDを渡せば、それをプライマリキーとしてデータをフェッチし自身が行オブジェクトとなります。クラスモデルは図2のようなイメージになります。
まずはルールどおりテストケースクラスを定義しましょう。Employeeクラスのテストケースなのでファイル名をEmployeeTest.phpとし、クラス名称をEmployeeTestとします。
継承するクラスPHPUnit_Framework_TestCaseは、PEARディレクトリ直下のPHPUnit/Framework/TestCase.phpファイルで定義されていますが、PHPUnitでは、PHPUnit/Framework.phpというファイルがフレームワークのヘッダファイルという位置付けですので、こちらのファイルだけをインクルードします。
require_once 'PHPUnit/Framework.php'; require_once 'Employee.php'; class EmployeeTest extends PHPUnit_Framework_TestCase { }
次に準備処理を記述します。今回はEmployeeクラスのインスタンス化をsetUp()メソッドで行い、テストケースのプロパティとして保持します。setUp()メソッドはテストメソッドが実行される直前に毎回呼び出されるため、テストのたびにインスタンス化する手間が省けます。
●EmployeeTest.php
public function setUp() { $this->employee = new Employee; }
次にテストメソッドを追加します。まずは基本的な振る舞いが正しく動作するかどうかのテストとして、Employeeプロパティのアクセサメソッドと保存メソッドをテストします。
実際にはまだEmployeeクラスは存在しませんが、クラスメソッドの仕様は図2で確定していますので、クラスが出来上がっている前提でこれらのメソッドを呼び出し、実行結果をテストしてみましょう。
セッターメソッドsetName()とsetAge()で各プロパティセットし直後にゲッターメソッドgetName()とgetAge()で同じ値が返るかどうか、また、save()メソッドの返り値がTRUEであるということをアサーションする基本的な動作テストです。
public function testSaveObject() { // プロパティをセット $this->employee->setName('SHIGETA Takuji'); $this->employee->setAge(20); // (1)ゲッターメソッドの返り値テスト $this->assertEquals('SHIGETA Takuji', $this->employee->getName()); $this->assertEquals(20, $this->employee->getAge()); // (2)保存の結果テスト $result = $this->employee->save(); $this->assertTrue($result); }
1では期待値と結果の二値間比較をするアサーションメソッドassertEquals()を使用しています。assertEquals()は、両変数の型まで比較することに注意しましょう。型が違っていればテストは失敗となります。ここでは直前にセットした値がそのまま返されるかどうかをテストしています。
2では、save()メソッドは実行結果を真偽値で返す仕様で作りますので、TRUEであることをテストするアサーションメソッドassertTrue()を使用しています。こちらも厳格な型としてTRUEを判断しますので、文字列や数値が返ればテストは失敗です。
では、次はもう少し複合的なテストを書いてみましょう。追加したオブジェクトが正しく取得できるかどうかをテストします。save()後にgetPrimaryKey()メソッドでプライマリキーを取得し、そのキーを元に行オブジェクトを生成し、保存したプロパティが取得できるかどうかのテストです。
public function testSaveObjectAndFetchRow() { $this->employee->setName('SHIGETA Takuji'); $this->employee->save(); // (1)保存したプライマリキーをテスト $pKey = $this->employee->getPrimaryKey(); $this->assertType('integer', $pKey); // (2)再取得したオブジェクトのテスト $employee = new Employee($pKey); $this->assertEquals('SHIGETA Takuji', $employee->getName()); }
1ではinteger型のプライマリーキーが正しく取得できているかをassertTypeメソッドで型をチェックし、2では、取得オブジェクトのgetName()が挿入時の文字列を返すかどうかをassertEquals()メソッドでチェックしています。
PHPUnitで用意されているアサーションメソッドはPHPUnit/Framework/Assert.phpで定義されています。メソッド名は自然言語によって分かりやすくなっている上にメソッドのコメントも詳細に記述されています。以下によく使うアサーションメソッドをいくつか挙げておきます。
メソッド名 | 説明 |
---|---|
assertTrue($var) | $varがTRUEである |
assertFalse($var) | $varがFALSEである |
assertNull($var) | $varがNULLである |
assertFileExists($file) | $fileが存在する |
assertEquals($expect, $var) | $varが$expectと等しい |
assertArrayHasKey($key, $array) | 配列$arrayにキー$keyが存在する |
assertObjectHasAttribute($attr, $object) | オブジェクト$objectにプロパティ変数$attrが存在する |
assertGreaterThan($expect, $var) | $expect < $var が成立する |
assertGreaterThanOrEqual($expect, $var) | $expect <= $var が成立する |
assertLessThan($expect, $var) | $expect > $var が成立する |
assertLessThanOrEqual($expect, $var) | $expect >= $var が成立する |
Copyright © ITmedia, Inc. All Rights Reserved.