Prev Next

第22章 PHPUnit の拡張

テストを書きやすくする、あるいはテストの実行結果の表示方法を変更するなど、 PHPUnit はさまざまな方法で拡張することができます。 PHPUnit を拡張するための第一歩をここで説明します。

PHPUnit_Framework_TestCase のサブクラスの作成

PHPUnit_Framework_TestCase を継承した抽象サブクラスにユーティリティメソッドを書き、 そのクラスをさらに継承してテストクラスを作成します。 これが、PHPUnit を拡張するための一番簡単な方法です。

アサートクラスの作成

あなたの目的に合った独自のアサーションを実装したクラスを作成します。

PHPUnit_Extensions_TestDecorator のサブクラスの作成

PHPUnit_Extensions_TestDecorator のサブクラスでテストケースあるいはテストスイートをラッピングし、 デコレータパターンを使用することで 各テストの実行前後に何らかの処理をさせることができます。

PHPUnit には、PHPUnit_Extensions_RepeatedTest および PHPUnit_Extensions_TestSetup という 2 つの具象テストデコレータが付属しています。 前者はテストを繰り返し実行し、それらが全て成功した場合にのみ成功とみなします。 後者については 第 6 章 で説明しました。

例 22.1 は、テストデコレータ PHPUnit_Extensions_RepeatedTest の一部を抜粋したものです。独自のデコレータを作成するための参考にしてください。

例 22.1: RepeatedTest デコレータ

<?php
require_once 'PHPUnit/Extensions/TestDecorator.php';

class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator
{
private $timesRepeat = 1;

public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1)
{
parent::__construct($test);

if (is_integer($timesRepeat) &&
$timesRepeat >= 0) {
$this->timesRepeat = $timesRepeat;
}
}

public function count()
{
return $this->timesRepeat * $this->test->count();
}

public function run(PHPUnit_Framework_TestResult $result = NULL)
{
if ($result === NULL) {
$result = $this->createResult();
}

for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) {
$this->test->run($result);
}

return $result;
}
}
?>

PHPUnit_Framework_Test の実装

PHPUnit_Framework_Test インターフェイスの機能は限られており、 実装するのは簡単です。PHPUnit_Framework_Test を実装するのは PHPUnit_Framework_TestCase の実装より単純で、 これを用いて例えば データ駆動のテスト (data-driven tests) などを実行します。

カンマ区切り (CSV) ファイルの値と比較する、データ駆動のテストを 例 22.2 に示します。このファイルの各行は foo;bar のような形式になっており (訳注: CSV じゃない……)、 最初の値が期待値で 2 番目の値が実際の値です。

例 22.2: データ駆動のテスト

<?php
require_once 'PHPUnit/Framework.php';
require_once 'PHPUnit/Util/Timer.php';
require_once 'PHPUnit/TextUI/TestRunner.php';

class DataDrivenTest implements PHPUnit_Framework_Test
{
private $lines;

public function __construct($dataFile)
{
$this->lines = file($dataFile);
}

public function count()
{
return 1;
}

public function run(PHPUnit_Framework_TestResult $result = NULL)
{
if ($result === NULL) {
$result = new PHPUnit_Framework_TestResult;
}

foreach ($this->lines as $line) {
$result->startTest($this);
PHPUnit_Util_Timer::start();

list($expected, $actual) = explode(';', $line);

try {
PHPUnit_Framework_Assert::assertEquals(trim($expected), trim($actual));
}

catch (PHPUnit_Framework_AssertionFailedError $e) {
$result->addFailure($this, $e, PHPUnit_Util_Timer::stop());
}

catch (Exception $e) {
$result->addError($this, $e, PHPUnit_Util_Timer::stop());
}

$result->endTest($this, PHPUnit_Util_Timer::stop());
}

return $result;
}
}

$test = new DataDrivenTest('data_file.csv');
$result = PHPUnit_TextUI_TestRunner::run($test);
?>
PHPUnit 3.2.10 by Sebastian Bergmann.

.F

Time: 0 seconds

There was 1 failure:

1) DataDrivenTest
Failed asserting that two strings are equal.
expected string <bar>
difference      <  x>
got string      <baz>
/home/sb/DataDrivenTest.php:32
/home/sb/DataDrivenTest.php:53

FAILURES!
Tests: 2, Failures: 1.

PHPUnit_Framework_TestResult のサブクラスの作成

独自の PHPUnit_Framework_TestResult オブジェクトを run() メソッドに渡すと、 テストの実行方法や収集されるテスト結果を変更することができます。

PHPUnit_Framework_TestListener の実装

テスト結果をカスタマイズするために、必ず PHPUnit_Framework_TestResult のサブクラスを書かなければならないというわけではありません。たいていは、 新しい PHPUnit_Framework_TestListener を実装して (表 21.14 を参照ください)、 テストの前にそれを PHPUnit_Framework_TestResult オブジェクトにアタッチするだけで十分です。

例 22.3 は、PHPUnit_Framework_TestListener インターフェイスを実装する単純な例です。

例 22.3: シンプルなテストリスナー

<?php
require_once 'PHPUnit/Framework.php';

class SimpleTestListener
implements PHPUnit_Framework_TestListener
{
public function
addError(PHPUnit_Framework_Test $test,
Exception $e,
$time)
{
printf(
"テスト '%s' の実行中にエラーが発生しました。\n",
$test->getName()
);
}

public function
addFailure(PHPUnit_Framework_Test $test,
PHPUnit_Framework_AssertionFailedError $e,
$time)
{
printf(
"テスト '%s' に失敗しました。\n",
$test->getName()
);
}

public function
addIncompleteTest(PHPUnit_Framework_Test $test,
Exception $e,
$time)
{
printf(
"テスト '%s' は未完了です。\n",
$test->getName()
);
}

public function
addSkippedTest(PHPUnit_Framework_Test $test,
Exception $e,
$time)
{
printf(
"テスト '%s' は省略されました。\n",
$test->getName()
);
}

public function startTest(PHPUnit_Framework_Test $test)
{
printf(
"テスト '%s' が開始されました。\n",
$test->getName()
);
}

public function endTest(PHPUnit_Framework_Test $test, $time)
{
printf(
"テスト '%s' が終了しました。\n",
$test->getName()
);
}

public function
startTestSuite(PHPUnit_Framework_TestSuite $suite)
{
printf(
"テストスイート '%s' が開始されました。\n",
$suite->getName()
);
}

public function
endTestSuite(PHPUnit_Framework_TestSuite $suite)
{
printf(
"テストスイート '%s' が終了しました。\n",
$suite->getName()
);
}
}
?>

例 22.4 は、テストスイートを実行して監視する方法を示したものです。

例 22.4: テストスイートの実行と監視

<?php
require_once 'PHPUnit/Framework.php';

require_once 'ArrayTest.php';
require_once 'SimpleTestListener.php';

// ArrayTest クラスのテストを含む
// テストスイートを作成します。
$suite = new PHPUnit_Framework_TestSuite('ArrayTest');

// テスト結果オブジェクトを作成し、そこにオブザーバとして
// SimpleTestListener をアタッチします。
$result = new PHPUnit_Framework_TestResult;
$result->addListener(new SimpleTestListener);

// テストを実行します。
$suite->run($result);
?>
テストスイート 'ArrayTest' が開始されました。
テスト 'testNewArrayIsEmpty' が開始されました。
テスト 'testNewArrayIsEmpty' が終了しました。
テスト 'testArrayContainsAnElement' が開始されました。
テスト 'testArrayContainsAnElement' が終了しました。
テストスイート 'ArrayTest' が終了しました。

新しいテストランナーの作成

テストの実行結果を異なる方法で受け取りたい場合には、 独自のテストランナーを作成します。その際の初めの一歩となるのが、 PHPUnit_TextUI_TestRunner クラス (PHPUnit のコマンドライン版テストランナー) の親クラスである抽象クラス PHPUnit_Runner_BaseTestRunner です。

Prev Next
1. 自動テスト
2. PHPUnit の目標
3. PHPUnit のインストール
4. PHPUnit 用のテストの書き方
データプロバイダ
例外のテスト
PHP のエラーのテスト
5. コマンドラインのテストランナー
6. Fixtures
tearDown() よりも setUp()
バリエーション
Fixture の共有
7. テストの構成
スイートレベルのセットアップ
8. テストケースの拡張
出力内容のテスト
パフォーマンス低下のテスト
9. データベースのテスト
データセット
Flat XML データセット
XML データセット
操作
データベースのテストのコツ
10. 不完全なテスト・テストの省略
不完全なテスト
テストの省略
11. モックオブジェクト
自己シャント
スタブ
12. テストの進め方
開発中のテスト
デバッグ中のテスト
13. テストファーストプログラミング
銀行口座の例
14. コードカバレッジ解析
カバーするメソッドの指定
コードブロックの無視
ファイルのインクルードや除外
15. テストのその他の使用法
アジャイルな文書作成
複数チームでのテスト
16. ログ出力
XML 形式
コードカバレッジ (XML)
JavaScript Object Notation (JSON)
Test Anything Protocol (TAP)
GraphViz マークアップ
テストデータベース
17. 雛形ジェネレータ
アノテーション
18. PHPUnit と Selenium
Selenium RC
PHPUnit_Extensions_SeleniumTestCase
19. 継続的インテグレーション
CruiseControl
phpUnderControl
Apache Maven
20. PHPUnit の実装
21. PHPUnit API
概要
PHPUnit_Framework_Assert
PHPUnit_Framework_Test
PHPUnit_Framework_TestCase
PHPUnit_Framework_TestSuite
PHPUnit_Framework_TestResult
パッケージの構成
22. PHPUnit の拡張
PHPUnit_Framework_TestCase のサブクラスの作成
アサートクラスの作成
PHPUnit_Extensions_TestDecorator のサブクラスの作成
PHPUnit_Framework_Test の実装
PHPUnit_Framework_TestResult のサブクラスの作成
PHPUnit_Framework_TestListener の実装
新しいテストランナーの作成
A. アサーション
B. XML 設定ファイル
テストスイート
グループ
コードカバレッジ対象のファイルの追加や除外
ログ出力
PMD ルール
PHP INI 項目やグローバル変数の設定
C. PHP 4 用の PHPUnit
D. 目次
E. 参考文献
F. 著作権