製作著作 © 2005, 2006, 2007, 2008, 2009 Sebastian Bergmann
PHPUnit 3.2 対応版 Updated on 2009-12-29.
どんなにすぐれたプログラマも、間違いを犯します。 よいプログラマとそうでないプログラマの違いは、 よいプログラマはテストを行って間違いをできるだけ早く発見してしまうことです。 テストをするのが早ければ早いほど間違いを発見しやすくなり、 またそれを修正しやすくなります。 リリース直前までテストを先延ばしにしておくことが非常に問題であるのはこのためです。 そんなことをすると、すべてのエラーを発見しきることができず、 発見したエラーを修正することも非常に難しくなります。結局は、 トリアージを行ってどのエラーに対応するかを判断しなければならなくなります。 なぜならすべてのエラーを完全に修正することは不可能だからです。
PHPUnit を使用したテストは、全体としてはあなたがこれまでに行ってきたことと同じです。 ただ、そのやり方が違うだけです。それは、テスト つまりあなたのプログラムが期待通りにふるまうことを調べることと 総合テスト つまり実行可能なコード片がソフトウェアの各部分 (部品) を自動的にテストすることとの違いになります。実行可能なコード片のことを、 単体テスト (unit test) と呼びます。
この章では、単純な print
ベースのテストコードをもとにして完全な自動テストに書き換えていきます。
PHP 組み込みの array
をテストするように頼まれたとしましょう。このオブジェクトの機能のひとつに、
関数 sizeof() があります。新しく作成された配列では、
sizeof() 関数は 0 を返すはずです。
そして要素を 1 つ追加すると sizeof() は
1 を返すようになるはずです。テストしたい内容を
例 1.1
に示します。
例 1.1: Array および sizeof() のテスト
<?php
$fixture = array();
// $fixture は空のはずです。
$fixture[] = 'element';
// $fixture はひとつの要素を含むはずです。
?>
期待通りの結果が得られているかどうかを調べるためのいちばん単純な方法は、
要素を追加する前と後に sizeof() の結果を表示することです
(例 1.2 を参照ください)。
それぞれ 0 および 1 が得られたら、
array および sizeof()
が期待通りに動作していることになります。
例 1.2: print を使用した Array および sizeof() のテスト
<?php
$fixture = array();
print sizeof($fixture) . "\n";
$fixture[] = 'element';
print sizeof($fixture) . "\n";
?>
0 1
このテストは、成功したかどうかの判断を (出力結果を見て)
手動で行わなければなりません。今度は、この判断を自動でできるようにしてみましょう。
例 1.3 では、
期待される結果と実際の結果をコード中で比較して、もしそれらの値が等しければ
ok と表示します。もし not ok
と表示された場合は、どこかがおかしいということがわかります。
例 1.3: 期待値と実際の値を比較することによる Array および sizeof() のテスト
<?php
$fixture = array();
print sizeof($fixture) == 0 ? "ok\n" : "not ok\n";
$fixture[] = 'element';
print sizeof($fixture) == 1 ? "ok\n" : "not ok\n";
?>
ok ok
今度は、相違があった際に例外を発生させる関数を用意して、 期待値と実際の値を比較する処理を抽出してみましょう (例 1.4)。 これには 2 つの利点があります。テストが記述しやすくなること、 そして何か問題があったときにのみそれを出力させることができるということです。
例 1.4: アサーション関数を使用した Array および sizeof() のテスト
<?php
$fixture = array();
assertTrue(sizeof($fixture) == 0);
$fixture[] = 'element';
assertTrue(sizeof($fixture) == 1);
function assertTrue($condition)
{
if (!$condition) {
throw new Exception('Assertion failed.');
}
}
?>
これで、テストは完全に自動化されました。最初のバージョンでは単に テストする だけでしたが、このバージョンでは 自動テスト になっています。
自動テストを行う目的は、間違いを少なくすることです。 いくらすばらしいテストを行ったところで あなたのコードが完璧なものになるわけではありませんが、 自動テストを始めることで不具合の量を劇的に減らすことになるでしょう。 自動テストによってあなたのコードは信頼性の高いものとなり、大胆な設計変更 (リファクタリング) を行ったりチームメイトとの関係をよりよくしたり (複数チームでのテスト)、 その日の朝に比べて帰宅前のコードがよりよくなっていることを確信できたりといった効果があります。
いまのところ、組み込みの array および
sizeof() 関数のテストしかありません。
PHP が提供する数多くの array_*()
関数をテストしようとすると、それらそれぞれについてテストを記述する必要があります。
それらのすべてのテストについて基盤部分を最初から書いていくこともできますが、
共通部分は一度だけ記述するようにし、
個々のテストではテスト固有の部分のみを記述していくほうがずっとよい方法です。
PHPUnit は、そのような基盤部分を提供します。
PHPUnit のようなフレームワークには、解決しなければならない制約があります。 その中のいくつかはお互いに相反するものです。 テストは、以下の条件を同時に満たす必要があります。
テストの書き方を身に着けるのが難しければ、 開発者はそんなものを覚えようとしないでしょう。
テストが書きにくければ、開発者はそんなものを書こうとしないでしょう。
テストコードには、外部からの要素を含めるべきではありません。 そうするとテストコードが周りのノイズに埋もれてしまいます。
ボタン一発でテストが実行でき、 その結果は明白な形式で表示されなければなりません。
一日に何百何千というテストを実行できるよう、 テストはすばやく実行できなければなりません。
テストは、お互い他のテストに影響を及ぼしてはいけません。 テストの実行順序を変えることでテストの結果が変わってしまってはいけません。
テストの数や組み合わせを自由に選択できなければなりません。 テストが独立している以上、これは当然のことです。
これらの制約の中には、相反する項目が 2 点あります。
一般に、テストにはプログラミング言語の全機能を必要としません。 多くのテストツールは、テストを記述するため必要最小限の機能のみを組み込んだ 独自のスクリプト言語を提供しています。その結果、テストは読みやすく、 また書きやすいものになります。 なぜならテストの内容から気をそらせるノイズがないためです。しかし、 またひとつ新たなプログラミング言語やプログラミングツールの使い方を覚える必要があり、 不便です。
ひとつのテストが他のテストの結果に何の影響もおよぼさないようにするには、 各テストの実行前に毎回テスト用の環境を構築し、 終了後には毎回それを元の状態に戻す必要があります。しかし、環境を構築する (例: データベースに接続し、特定の状態を表すデータを投入する) には長い時間がかかります。
PHPUnit は、テスト言語として PHP を使用することで これらの衝突を回避しようとしています。小規模で単純なテストを行う際には、 PHP の機能は行き過ぎた面もあるかもしれません。しかし、PHP を使用することで、 これまでの開発経験や開発ツールを武器として利用できます。 あまり気乗りしないテスターを納得させるため、 テストを最初に書き始める際の負担をできるだけ下げることが重要だと考えています。
PHPUnit では、実行速度よりもテストの独立性を重視しています。 独立したテストに価値があるのは、そのほうがより高品質のフィードバックが得られるからです。 一連のテストの最初のほうで失敗したことでその後のすべてのテストが失敗してしまい、 大量の失敗報告を受け取るようなことがなくなります。 このオリエンテーションでは独立したテストを目指し、 シンプルなオブジェクトを数多く作るという設計を心がけます。 各オブジェクトは独立してすばやくテストできます。結果としてよりよい設計 に加えてより高速なテストが可能となります。
PHPUnit ではほとんどのテストが成功することを想定しており、 成功したテストの詳細について報告することはあまり価値がないと考えています。 テストが失敗した場合には、そのことをしっかり報告しなければなりません。 大半のテストは成功すべきであり、 それらについては実行したテストの数以外に特にコメントすべき情報はありません。 これは、PHPUnit のコアではなく報告用のクラス群に組み込まれている機能です。 テストの結果が表示される際には実行したテストの数が表示されますが、 詳細が表示されるのは失敗したテストについてだけです。
テストは決め細やかに行うこと、つまりひとつのテストではひとつのオブジェクトの ひとつの機能についてをテストするようにすることが期待されています。 そのため、最初のテストは失敗し、テストの実行は終了し、 PHPUnit は失敗を報告します。多くの小さなテストを実行させることは一種の芸術です。 決め細やかなテストにより、システム全体の設計がよりよいものとなります。
PHPUnit でオブジェクトをテストする際には、 その公開インターフェースについてのみテストを行います。 公開されている振る舞いにのみ基づいてテストを行うことで、 設計上の困難な問題により早い段階で対応できるようになり、 設計ミスがシステムの大部分に影響を及ぼすことを避けられます。
PHPUnit をインストールするには、 PEAR インストーラ を使用します。このインストーラは PEAR の根幹をなすものであり、 PHP のパッケージを配布する仕組みを提供しています。また、バージョン 4.3.0 以降のすべての PHP に同梱されています。
PHPUnit の配布に使用する PEAR チャネル (pear.phpunit.de)
を、ローカルの PEAR 環境に登録する必要があります。
pear channel-discover pear.phpunit.deこれを行う必要があるのは最初の一度だけです。これで、PEAR インストーラは PHPUnit チャネルからパッケージをインストールできるようになります。
pear install phpunit/PHPUnit
インストールすると、PHPUnit のソースファイルがローカルの PEAR
ディレクトリに格納されます。場所は、通常は
/usr/lib/php/PHPUnit です。
PHPUnit がサポートしているのは PEAR インストーラを使用する方法のみですが、 PHPUnit を手動でインストールすることも可能です。そのためには、 以下の手順に従ってください。
http://pear.phpunit.de/get/
からアーカイブをダウンロードし、それを php.ini
設定ファイルの include_path
で指定したディレクトリに展開します。
phpunit スクリプトを準備します。
pear-phpunit スクリプトの名前を
phpunit に変更します。
その中の @php_bin@ という文字列を、
PHP コマンドラインインタプリタへのパス (通常は
/usr/bin/php) に変更します。
それを PATH の通ったディレクトリにコピーし、
実行可能属性を付与します (chmod +x phpunit)。
PHPUnit/Util/Fileloader.php スクリプトを準備します。
その中の @php_bin@ という文字列を、
PHP コマンドラインインタプリタへのパス (通常は
/usr/bin/php) に変更します。
例 4.1 は、PHPUnit を使用する形式で 例 1.4 の 2 つのテストを書き直したものです。
例 4.1: PHPUnit を使用した Array および sizeof() のテスト
<?php
require_once 'PHPUnit/Framework.php';
class ArrayTest extends PHPUnit_Framework_TestCase
{
public function testNewArrayIsEmpty()
{
// 配列を作成します。
$fixture = array();
// 配列のサイズは 0 です。
$this->assertEquals(0, sizeof($fixture));
}
public function testArrayContainsAnElement()
{
// 配列を作成します。
$fixture = array();
// 配列にひとつの要素を追加します。
$fixture[] = 'Element';
// 配列のサイズは 1 です。
$this->assertEquals(1, sizeof($fixture));
}
}
?>
Whenever you are tempted to type something into a
何かを | ||
| --Martin Fowler | ||
例 4.1 では、 PHPUnit を使用してテストを記述する基本手順を説明しています。
Class という名前のクラスのテストは、ClassTest という名前のクラスに記述します。
ClassTest は、(ほとんどの場合) PHPUnit_Framework_TestCase を継承します。
テストは、test* という名前のパブリックメソッドとなります。
あるいは、@test アノテーションをメソッドのコメント部で使用することで、それがテストメソッドであることを示すこともできます。
テストメソッドの中で assertEquals() のようなアサーションメソッド (表 21.1 を参照ください) を使用して、期待される値と実際の値が等しいことを確かめます。
テストメソッドには任意の引数を渡すことができます。
この引数は、データプロバイダメソッド
(例 4.2
の provider())
で指定します。使用するデータプロバイダメソッドを指定するには
@dataProvider アノテーションを使用します。
データプロバイダメソッドは、public かつ
static でなければなりません。また、
メソッドの返り値の型は、配列の配列あるいはオブジェクト
(Iterator インターフェイスを実装しており、
反復処理の際に配列を返すもの) である必要があります。
この返り値の各要素に対して、その配列の中身を引数としてテストメソッドがコールされます。
例 4.2: データプロバイダの使用
<?php
class DataTest extends PHPUnit_Framework_TestCase
{
public static function provider()
{
return array(
array(0, 0, 0),
array(0, 1, 1),
array(1, 0, 1),
array(1, 1, 3)
);
}
/**
* @dataProvider provider
*/
public function testAdd($a, $b, $c)
{
$this->assertEquals($c, $a + $b);
}
}
?>
phpunit DataTest
PHPUnit 3.2.10 by Sebastian Bergmann.
...F
Time: 0 seconds
There was 1 failure:
1) testAdd(DataTest) with data (1, 1, 3)
Failed asserting that <integer:2> matches expected value <integer:3>.
/home/sb/DataTest.php:21
FAILURES!
Tests: 4, Failures: 1.
例 4.3
は、テストするコード内で例外がスローされたかどうかを
@expectedException アノテーションを使用して調べる方法を示すものです。
例 4.3: @expectedException アノテーションの使用法
<?php
require_once 'PHPUnit/Framework.php';
class ExceptionTest extends PHPUnit_Framework_TestCase
{
/**
* @expectedException InvalidArgumentException
*/
public function testException()
{
}
}
?>
phpunit ExceptionTest
PHPUnit 3.2.10 by Sebastian Bergmann.
F
Time: 0 seconds
There was 1 failure:
1) testException(ExceptionTest)
Expected exception InvalidArgumentException
FAILURES!
Tests: 1, Failures: 1.
一方、setExpectedException()
メソッドを使用して、発生するであろう例外を指定することもできます。この方法を
例 4.4
に示します。
例 4.4: テスト対象のコードで発生するであろう例外の指定
<?php
require_once 'PHPUnit/Framework.php';
class ExceptionTest extends PHPUnit_Framework_TestCase
{
public function testException()
{
$this->setExpectedException('InvalidArgumentException');
}
}
?>
phpunit ExceptionTest
PHPUnit 3.2.10 by Sebastian Bergmann.
F
Time: 0 seconds
There was 1 failure:
1) testException(ExceptionTest)
Expected exception InvalidArgumentException
FAILURES!
Tests: 1, Failures: 1.表 4.1 は、例外をテストするために用意されているメソッドをまとめたものです。
表4.1 例外のテスト用のメソッド
| メソッド | 意味 |
|---|---|
void setExpectedException(string $exceptionName) | 発生することを期待する例外の名前を $exceptionName に設定します。 |
String getExpectedException() | 発生することを期待する例外の名前を返します。 |
一方、 例 4.5 のような方法で例外をテストすることもできます。
例 4.5: 例外をテストするための、別の方法
<?php
require_once 'PHPUnit/Framework.php';
class ExceptionTest extends PHPUnit_Framework_TestCase {
public function testException() {
try {
// ... 例外が発生するであろうコード ...
}
catch (InvalidArgumentException $expected) {
return;
}
$this->fail('期待通りの例外が発生しませんでした。');
}
}
?>
例外が発生するはずの
例 4.5
のコードで例外が発生しなかった場合、それに続く
fail() (表 21.3 を参照ください)
によってテストが終了し、問題を報告します。期待通りに例外が発生すると、
catch ブロックが実行されてテストは正常終了します。
デフォルトでは、PHPUnit はテストの実行中に発生した PHP のエラーや警告そして notice を例外に変換します。これらの例外を用いて、たとえば 例 4.6 のように PHP のエラーが発生することをテストできます。
例 4.6: @expectedException を用いた、PHP エラーが発生することのテスト
<?php
class ExpectedErrorTest extends PHPUnit_Framework_TestCase
{
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testFailingInclude()
{
include 'not_existing_file.php';
}
}
?>
phpunit ExpectedErrorTest
PHPUnit 3.2.21 by Sebastian Bergmann.
.
Time: 0 seconds
OK (1 test)
PHPUnit_Framework_Error_Notice および
PHPUnit_Framework_Error_Warning は、
それぞれ PHP の notice と警告に対応します。
phpunit コマンドを実行すると、PHPUnit
のコマンドライン版テストランナーが起動します。
コマンドラインのテストランナーを使用したテストの様子を以下に示します。
phpunit ArrayTest
PHPUnit 3.2.10 by Sebastian Bergmann.
..
Time: 0 seconds
OK (2 tests)テストがひとつ実行されるたびに、PHPUnit コマンドラインツールはその経過を示す文字を出力します。
PHPUnit は、失敗 (failures) と
エラー (errors) を区別します。
「失敗」は PHPUnit のアサーションに違反した場合、つまり例えば
assertEquals() のコールに失敗した場合などで、
「エラー」は予期せぬ例外や PHP のエラーが発生した場合となります。
この区別は、時に有用です。というのは「エラー」は一般的に「失敗」
より修正しやすい傾向があるからです。
もし大量の問題が発生した場合は、まず「エラー」を最初に片付け、
その後で「失敗」を修正していくのが最良の方法です。
以下のコードで、コマンドライン版テストランナーのスイッチの一覧を見てみましょう。
phpunit --help
PHPUnit 3.2.10 by Sebastian Bergmann.
Usage: phpunit [switches] UnitTest [UnitTest.php]
--log-graphviz <file> Log test execution in GraphViz markup.
--log-json <file> Log test execution in JSON format.
--log-tap <file> Log test execution in TAP format to file.
--log-xml <file> Log test execution in XML format to file.
--log-metrics <file> Write metrics report in XML format.
--log-pmd <file> Write violations report in PMD XML format.
--coverage-html <dir> Generate code coverage report in HTML format.
--coverage-xml <file> Write code coverage information in XML format.
--test-db-dsn <dsn> DSN for the test database.
--test-db-log-rev <r> Revision information for database logging.
--test-db-prefix ... Prefix that should be stripped from filenames.
--test-db-log-info ... Additional information for database logging.
--testdox-html <file> Write agile documentation in HTML format to file.
--testdox-text <file> Write agile documentation in Text format to file.
--filter <pattern> Filter which tests to run.
--group ... Only runs tests from the specified group(s).
--exclude-group ... Exclude tests from the specified group(s).
--loader <loader> TestSuiteLoader implementation to use.
--repeat <times> Runs the test(s) repeatedly.
--tap Report test execution progress in TAP format.
--testdox Report test execution progress in TestDox format.
--no-syntax-check Disable syntax check of test source files.
--stop-on-failure Stop execution upon first error or failure.
--verbose Output more verbose information.
--wait Waits for a keystroke after each test.
--skeleton Generate skeleton UnitTest class for Unit in Unit.php.
--help Prints this usage information.
--version Prints the version and exits.
--configuration <file> Read configuration from XML file.
-d key[=value] Sets a php.ini value.phpunit UnitTest
UnitTest という名前のクラスで定義されている
テストを実行します。このクラスは、UnitTest.php
という名前のファイルの中に定義されているものとします。
UnitTest は、PHPUnit_Framework_TestCase
を継承したクラスであるか、あるいは PHPUnit_Framework_Test
オブジェクト、例えば PHPUnit_Framework_TestSuite
のインスタンスを返す public static suite()
というメソッドを保持するクラスでなければなりません。
phpunit UnitTest UnitTest.php
UnitTest という名前のクラスで定義されているテストを実行します。
このクラスは、指定したファイルの中で定義されているものとします。
--log-graphviz
GraphViz
のマークアップを使用して、テストの実行結果のログを作成します。
生成されたログファイルは、例えば
dot などを使用して表示することが可能です。
詳細は 第 16 章 を参照ください。
このパラメータは、PEAR パッケージ Image_GraphViz
がインストールされている場合にのみ使用可能となることに注意しましょう。
--log-json--log-tapTest Anything Protocol (TAP) フォーマットを使用して、テストの実行結果のログを作成します。 詳細は 第 16 章 を参照ください。
--log-xmlテストの実行結果を XML 形式のログファイルに出力します。 詳細は 第 16 章 を参照ください。
--log-metricsテスト中のシステムに関するソフトウェアメトリクスを表す XML 形式のログファイルを作成します。
このパラメータは、tokenizer 拡張モジュールおよび Xdebug 拡張モジュールがインストールされている場合にのみ使用可能となることに注意しましょう。
--log-pmdたとえばソフトウェアメトリクスなどにもとづいて、 特定の規則に違反しているものを表す XML 形式のログファイルを作成します。
このパラメータは、tokenizer 拡張モジュールおよび Xdebug 拡張モジュールがインストールされている場合にのみ使用可能となることに注意しましょう。
--coverage-xmlテスト結果から XML 形式のログファイルを作成し、 コードカバレッジ情報もそこに含めます。 詳細は 第 16 章 を参照ください。
このパラメータは、tokenizer 拡張モジュールおよび Xdebug 拡張モジュールがインストールされている場合にのみ使用可能となることに注意しましょう。
--coverage-htmlコードカバレッジレポートを HTML 形式で作成します。 詳細は 第 14 章 を参照ください。
このパラメータは、tokenizer 拡張モジュールおよび Xdebug 拡張モジュールがインストールされている場合にのみ使用可能となることに注意しましょう。
--charset
--report で使用する文字セットを指定します。
--test-db-*テストの結果やコードカバレッジデータをデータベースに書き込みます。 詳細は 第 16 章 を参照ください。
このパラメータは、PDO 拡張モジュールがインストールされている場合にのみ使用可能となることに注意しましょう。
--testdox-html および --testdox-text実行したテストについて、HTML あるいはプレーンテキスト形式のドキュメントを生成します 詳細は 第 15 章 を参照ください。
--filter指定したパターンにマッチする名前のテストのみを実行します。 パターンとして指定できるのは、単一のテスト名か、 あるいは複数のテスト名にマッチする 正規表現 です。
--group
指定したグループのテストのみを実行します。
あるテストを特定のグループに所属させるには、
@group アノテーションを使用します。
--exclude-group
指定したグループをテストの対象外とします。
あるテストを特定のグループに所属させるには、
@group アノテーションを使用します。
--loader
PHPUnit_Runner_TestSuiteLoader を実装したクラスのうち、
実際に使用するものを指定します。
標準のテストスイートローダーは、現在の作業ディレクトリおよび PHP
の設定項目 include_path
で指定されているディレクトリからソースファイルを探します。
PEAR の命名規則に従い、Project_Package_Class
クラスがソースファイル Project/Package/Class.php
に対応します。
--repeat指定された回数だけ、繰り返しテストを実行します。
--no-syntax-checkテストのソースファイルの構文チェックを無効にします。
--tapTest Anything Protocol (TAP) を使用して、テストの進行状況を報告します。 詳細は 第 16 章 を参照ください。
--testdoxテストの進行状況を、アジャイルな文書として報告します。 詳細は 第 15 章 を参照ください。
--verboseより詳細な情報を出力します。例えば、 未完成のテストや省略したテストの名前が表示されます。
--wait各テストが終了するたびにキー入力待ちの状態になります。これは、 テストランナーが終了すると同時にウィンドウが閉じてしまうような場合に便利です。
--skeleton
(UnitTest.php に記述された) Unit
クラスに対して、テストケースクラス UnitTest
の雛形を記述したファイル UnitTest.php を作成します。
詳細は 第 17 章 を参照ください。
--configuration設定を XML ファイルから読み込みます。 詳細は 付録 B を参照ください。
-d指定した PHP 設定オプションの値を設定します。
テストを記述する際にいちばん時間を食うのは、テストを開始するための事前設定と テスト終了後の後始末の処理を書くことです。この事前設定は、テストの fixture と呼ばれます。
例 4.1
では、fixture は
$fixture という変数に格納された配列だけでした。
しかしたいていの場合は fixture はこれより複雑なものとなり、
それを準備するにはかなりの量のコードが必要です。本来のテストの内容が、
fixture を設定するためのコードの中に埋もれてしまうことになります。
この問題は、複数のテストで同じような fixture を設定する場合により顕著になります。
テストフレームワークの助けがなければ、
個々のテストのなかで同じような準備コードを繰り返し書くはめになってしまいます。
PHPUnit は、準備用のコードの共有をサポートしています。
各テストメソッドが実行される前に、setUp()
という名前のテンプレートメソッドが実行されます。setUp()
は、テスト対象のオブジェクトを生成するような処理に使用します。
テストメソッドの実行が終了すると、それが成功したか否かにかかわらず、
tearDown() という名前の別のテンプレートメソッドが実行されます。
tearDown() では、テスト対象のオブジェクトの後始末などを行います。
それでは、setUp() を使用してコードの重複を排除するように
例 4.1
を書き換えてみましょう。
まず最初にインスタンス変数 $fixture を宣言し、
メソッド内のローカル変数ではなくこちらを使用するようにします。
そして、array fixture の生成処理を
setUp() メソッドに移動します。最後に、
テストメソッド内で重複しているコードを取り除き、
新しく作成したインスタンス変数を使用するようにします。つまり、
assertEquals() で使用しているローカル変数
$fixture を、$this->fixture
に置き換えます。
例 6.1: setUp() を使用して Array fixture を作成する
<?php
require_once 'PHPUnit/Framework.php';
class ArrayTest extends PHPUnit_Framework_TestCase
{
protected $fixture;
protected function setUp()
{
// Array fixture を作成します。
$this->fixture = array();
}
public function testNewArrayIsEmpty()
{
// Array fixture のサイズは 0 のはずです。
$this->assertEquals(0, sizeof($this->fixture));
}
public function testArrayContainsAnElement()
{
// Array fixture に要素を追加します。
$this->fixture[] = 'Element';
// Array fixture のサイズは 1 のはずです。
$this->assertEquals(1, sizeof($this->fixture));
}
}
?>
各テストメソッドが実行されるたびに、setUp()
および tearDown() が一度ずつコールされます。
「テストケース内の全テストメソッドについて一度だけコールするように
したほうがよいのではないか」とお考えになるかもしれませんが、
そのようにすると各テストを互いに独立した状態にすることが難しくなります。
テストメソッドごとに setUp() および
tearDown() が一度ずつ実行されるだけでなく、
テストメソッドごとに、新しいテストケースクラスのインスタンスが作成されます
(第 20 章 を参照ください)。
setUp() と tearDown()
は理屈上では対称的になるはずですが、実際にはそうではありません。実際には、
tearDown() を実装する必要があるのは setUp()
で外部リソース (ファイルやソケットなど) を割り当てた場合のみです。もし
setUp() で単に PHP オブジェクトを作成しただけの場合は、
一般には tearDown() は必要ありません。しかし、もし
setUp() で大量のオブジェクトを作成した場合には、
それらの後始末をするために tearDown() で変数を
unset() したくなることもあるでしょう。
テストケースオブジェクト自体のガベージコレクションにはあまり意味がありません。
ふたつのテストがあって、それぞれの setup がほんの少しだけ違う場合にはどうなるでしょう? このような場合は、二種類の可能性が考えられます。
もし setUp() の違いがごくわずかなものなら、
その違う部分を setUp()
からテストメソッドのほうに移動させます。
setUp() の違いが大きければ、
テストケースクラスを別に分ける必要があります。それぞれのクラスには、
setup の違いを表す名前をつけます。
複数のテストの間で fixture を を共有する利点は、ほとんどありません。 しかし、設計上の問題などでどうしても fixture を共有しなければならないこともあるでしょう。
複数のテスト間で共有する意味のある fixture の例として意味のあるものといえば、 データベースとの接続でしょう。テストのたびに新しいデータベース接続を毎回作成するのではなく、 最初にログインした状態を再利用するということです。こうすることで、 テストの実行時間を短縮できます。
例 6.2
は、PHPUnit_Framework_TestSuite クラス
(「スイートレベルのセットアップ」 を参照ください)
のテンプレートメソッド
setUp() および tearDown() を使用します。
これらを使用して、
最初のテストの前にデータベースとの接続を行い、
最後のテストが終わった後にデータベースとの接続を解除します。
PHPUnit_Framework_TestSuite オブジェクトの属性
$sharedFixture が、
PHPUnit_Framework_TestSuite
および PHPUnit_Framework_TestCase
オブジェクトから使用できます。
例 6.2: テストスイートの複数テスト間での fixture の共有
<?php
require_once 'PHPUnit/Framework.php';
class DatabaseTestSuite extends PHPUnit_Framework_TestSuite
{
protected function setUp()
{
$this->sharedFixture = new PDO(
'mysql:host=wopr;dbname=test',
'root',
''
);
}
protected function tearDown()
{
$this->sharedFixture = NULL;
}
}
?>
このように fixture を共有することがテストの価値を下げてしまうということを、 まだうまく伝え切れていないかもしれません。問題なのは、 各オブジェクトが疎結合になっていないという設計なのです。 複数が連携しているようなテストを作って設計上の問題から目をそらしてしまうのではなく、 きちんと設計しなおした上で、スタブ (第 11 章 を参照ください) を使用するテストを書くことをお勧めします。
PHPUnit の目指すところ (第 2 章 を参照ください) のひとつに 「自由に組み合わせられる」ということがあります。つまり、 例えば「そのプロジェクトのすべてのテストを実行する」「プロジェクトの中の ある部品を構成するすべてのクラスについて、すべてのテストを実行する」 「特定のひとつのクラスのテストのみを実行する」など、 数や組み合わせにとらわれずに好きなテストを一緒に実行できるということです。
PHPUnit フレームワークの PHPUnit_Framework_TestSuite
クラスを使用すると、複数のテストを階層構造でテストスイートにまとめることができます。
実際の例として、PHPUnit 自身のテストスイートを見てみましょう。
例 7.1
は Tests/AllTests.php の一部を抜粋したもの、そして
例 7.2
は Tests/Framework/AllTests.php の一部を抜粋したものです。
例 7.1: AllTests クラス
<?php
require_once 'PHPUnit/Framework.php';
require_once 'Framework/AllTests.php';
// ...
class AllTests
{
public static function suite()
{
$suite = new PHPUnit_Framework_TestSuite('PHPUnit');
$suite->addTest(Framework_AllTests::suite());
// ...
return $suite;
}
}
?>
例 7.2: Framework_AllTests クラス
<?php
require_once 'PHPUnit/Framework.php';
require_once 'Framework/AssertTest.php';
// ...
class Framework_AllTests
{
public static function suite()
{
$suite = new PHPUnit_Framework_TestSuite('PHPUnit Framework');
$suite->addTestSuite('Framework_AssertTest');
// ...
return $suite;
}
}
?>
Framework_AssertTest クラスは、
基底クラス PHPUnit_Framework_TestCase
を継承した標準的なテストケースクラスです。
phpunit AllTests を
Tests ディレクトリで実行すると、すべてのテストを実行します。
phpunit Framework_AllTests AllTests.php を
Tests/Framework ディレクトリで実行すると、
PHPUnit_Framework_* クラスのテストのみを実行します。
phpunit Framework_AssertTest AssertTest.php を
Tests/Framework ディレクトリで実行すると、
PHPUnit_Framework_Assert クラスのテストのみを実行します。
phpunit --filter testFail Framework_AssertTest AssertTest.php を
Tests/Framework ディレクトリで実行すると、
Framework_AssertTest クラスの
testFail という名前のテストのみを実行します。
PHPUnit_Framework_TestSuite クラスには、
ふたつのテンプレートメソッド setUp()
および tearDown() が存在します。
これらはそれぞれ、テストスイートの最初のテストの実行前と
テストスイートの最後のテストの実行後にコールされます。
例 7.3: MySuite クラス
<?php
require_once 'MyTest.php';
class MySuite extends PHPUnit_Framework_TestSuite
{
public static function suite()
{
return new MySuite('MyTest');
}
protected function setUp()
{
print "\nMySuite::setUp()";
}
protected function tearDown()
{
print "\nMySuite::tearDown()";
}
}
?>
例 7.3
のテストスイート MySuite に追加されたテストケースクラス
MyTest には、ふたつのテストメソッド
testOne() と testTwo() のほかに
setUp() メソッドと tearDown()
メソッドが定義されているものとします。
この出力を見れば、これら 8 つのメソッドがどの順にコールされるのかがわかります。
MySuite::setUp() MyTest::setUp() MyTest::testOne() MyTest::tearDown() MyTest::setUp() MyTest::testTwo() MyTest::tearDown() MySuite::tearDown()
PHPUnit_Framework_TestSuite クラスの
setUp() メソッドで
$this->sharedFixture に保存した変数は、
テストスイートオブジェクトに登録したすべてのテストから
$this->sharedFixture でアクセスできます
(「Fixture の共有」 を参照ください)。
TestSuite の setUp()
および tearDown() メソッドは、
(たとえばフィルタリングなどの結果として)
テストスイート内のテストが実行されなかった場合でもコールされることに注意しましょう。
PHPUnit では、テストクラスの基底クラスである
PHPUnit_Framework_TestCase を拡張するための方法を提供しています。
これにより、出力内容やパフォーマンス低下のテストができるようになります。
メソッドの実行結果を確かめる方法として、(echo や
print などによる)
出力が期待通りのものかを調べたいこともあるでしょう。
PHPUnit_Extensions_OutputTestCase クラスは、PHP の
出力バッファリング 機能を使用してこの仕組みを提供します。
例 8.1
では、PHPUnit_Extensions_OutputTestCase
のサブクラスを作成し、期待する出力内容を expectOutputString()
メソッドで設定する方法を示します。
期待通りの出力が得られなかった場合は、そのテストは失敗という扱いになります。
例 8.1: PHPUnit_Extensions_OutputTestCase の使用法
<?php
require_once 'PHPUnit/Extensions/OutputTestCase.php';
class OutputTest extends PHPUnit_Extensions_OutputTestCase
{
public function testExpectFooActualFoo()
{
$this->expectOutputString('foo');
print 'foo';
}
public function testExpectBarActualBaz()
{
$this->expectOutputString('bar');
print 'baz';
}
}
?>
phpunit OutputTest
PHPUnit 3.2.10 by Sebastian Bergmann.
.F
Time: 0 seconds
There was 1 failure:
1) testExpectBarActualBaz(OutputTest)
Failed asserting that two strings are equal.
expected string <bar>
difference < x>
got string <baz>
FAILURES!
Tests: 2, Failures: 1.
表 8.1
PHPUnit_Extensions_OutputTestCase
が提供するメソッドをまとめたものです。
表8.1 OutputTestCase
| メソッド | 意味 |
|---|---|
void expectOutputRegex(string $regularExpression) | 出力が正規表現 $regularExpression にマッチするであろうという予測を設定します。 |
void expectOutputString(string $expectedString) | 出力が文字列 $expectedString と等しくなるであろうという予測を設定します。 |
bool setOutputCallback(callable $callback) | たとえば出力時の正規化などに使用するコールバック関数を設定します。 |
PHPUnit_Extensions_PerformanceTestCase
を継承したテストクラスを使用すると、
関数やメソッドの実行が制限時間内に終わったかどうかなどをテストすることができます。
PHPUnit_Extensions_PerformanceTestCase
のサブクラスを作成してその setMaxRunningTime()
メソッドを使用し、実行時間の最大値を制限する方法を
例 8.2
で示します。
もしテストが制限時間内に終了しなければ、そのテストは失敗という扱いになります。
例 8.2: PHPUnit_Extensions_PerformanceTestCase の使用法
<?php
require_once 'PHPUnit/Extensions/PerformanceTestCase.php';
class PerformanceTest extends PHPUnit_Extensions_PerformanceTestCase
{
public function testPerformance()
{
$this->setMaxRunningTime(2);
sleep(1);
}
}
?>
表 8.2
は、PHPUnit_Extensions_PerformanceTestCase
が実装しているメソッドをまとめたものです。
表8.2 PerformanceTestCase
| メソッド | 意味 |
|---|---|
void setMaxRunningTime(int $maxRunningTime) | テストの所要時間の最大値を (秒単位で) $maxRunningTime に設定します。 |
integer getMaxRunningTime() | このテストの最大所要時間を返します。 |
PHPUnit_Framework_TestCase には、これら以外にも
PHPUnit_Extensions_Database_TestCase や
PHPUnit_Extensions_SeleniumTestCase といった拡張があります。
これらについては、それぞれ 第 9 章
と 第 18 章 で説明します。
作成中のソフトウェアのテストを書いているうちに、 データベースに関するコードをテストする必要が出てくることもあるでしょう。 データベース拡張を使用すると、 データベースを特定の状態にしてからデータベース関連のコードを実行し、 データベースのデータが期待通りになっているかどうかを確かめることができます。
データベース関連のユニットテストを作成するもっとも手っ取り早い方法は、
PHPUnit_Extensions_Database_TestCase クラスを継承することです。
このクラスには、データベース接続を作成したり
データベースにデータを送信したり、
テストの実行後にデータベースの中身を様々な形式のデータセットと比較したりする機能があります。
例 9.1
に、getConnection() と getDataSet()
の実装例を示します。
例 9.1: データベーステストケースの準備
<?php
require_once 'PHPUnit/Extensions/Database/TestCase.php';
class DatabaseTest extends PHPUnit_Extensions_Database_TestCase
{
protected function getConnection()
{
$pdo = new PDO('mysql:host=localhost;dbname=testdb', 'root', '');
return $this->createDefaultDBConnection($pdo, 'testdb');
}
protected function getDataSet()
{
return $this->createFlatXMLDataSet(dirname(__FILE__).'/_files/bank-account-seed.xml');
}
}
?>
getConnection() メソッドは、
PHPUnit_Extensions_Database_DB_IDatabaseConnection
インターフェイスの実装を返す必要があります。
createDefaultDBConnection()
メソッドを使用して、データベース接続を返すことができます。
このメソッドの最初のパラメータには PDO
オブジェクトを渡し、2 番目のパラメータにはテスト対象のスキーマの名前を渡します。
getDataSet() メソッドは、
PHPUnit_Extensions_Database_DataSet_IDataSet
インターフェイスの実装を返す必要があります。
現在、PHPUnit では 3 種類のデータセットが使用できます。
データセットについては 「データセット」
で説明します。
表9.1 データベーステストケースのメソッド
| メソッド | 意味 |
|---|---|
PHPUnit_Extensions_Database_DB_IDatabaseConnection getConnection() | データベース接続を返すように実装します。これを用いて、期待するデータセットやテーブルを調べます。 |
PHPUnit_Extensions_Database_DataSet_IDataSet getDataSet() | データセットを返すように実装します。データベースの初期設定 (setup) や後始末 (teardown) の際にこれを使用します。 |
PHPUnit_Extensions_Database_Operation_DatabaseOperation getSetUpOperation() | オーバーライドして、各テストの最初に実行する特定の操作を返すようにします。操作の詳細については 「操作」 を参照ください。 |
PHPUnit_Extensions_Database_Operation_DatabaseOperation getTearDownOperation() | オーバーライドして、各テストの最後に実行する特定の操作を返すようにします。操作の詳細については 「操作」 を参照ください。 |
PHPUnit_Extensions_Database_DB_DefaultDatabaseConnection createDefaultDBConnection(PDO $connection, string $schema) | $connection PDO オブジェクトのデータベース接続ラッパーを返します。テスト対象のデータベーススキーマを $schema で指定します。このメソッドの結果を getConnection() の返り値として使用することができます。 |
PHPUnit_Extensions_Database_DataSet_FlatXmlDataSet createFlatXMLDataSet(string $xmlFile) | $xmlFile で指定した絶対パスにある xml ファイルから作成したフラット xml データセットを返します。xml ファイルについての詳細は 「Flat XML データセット」 を参照ください。このメソッドの結果を getDataSet() の返り値として使用することができます。 |
PHPUnit_Extensions_Database_DataSet_XmlDataSet createXMLDataSet(string $xmlFile) | $xmlFile で指定した絶対パスにある xml ファイルから作成した xml データセットを返します。xml ファイルについての詳細は 「XML データセット」 を参照ください。このメソッドの結果を getDataSet() の返り値として使用することができます。 |
void assertTablesEqual(PHPUnit_Extensions_Database_DataSet_ITable $expected, PHPUnit_Extensions_Database_DataSet_ITable $actual) | $expected テーブルの内容が $actual テーブルの内容と一致しないときにエラーを報告します。 |
void assertDataSetsEqual(PHPUnit_Extensions_Database_DataSet_IDataSet $expected, PHPUnit_Extensions_Database_DataSet_IDataSet $actual) | $expected データセットの内容が $actual データセットの内容と一致しないときにエラーを報告します。 |
新しいテストケースクラスを作成する際には、 これから書くべきテストの内容をはっきりさせるために、 まず最初は以下のような空のテストメソッドを書きたくなることでしょう。
public function testSomething()
{
}
しかし、PHPUnit フレームワークでは空のメソッドを「成功した」
と判断してしまうという問題があります。このような解釈ミスがあると、
テスト結果のレポートが無意味になってしまいます。
そのテストがほんとうに成功したのか、
それともまだテストが実装されていないのかが判断できないからです。
実装していないテストメソッドの中で $this->fail()
をコールするようにしたところで事態は何も変わりません。
こうすると、テストが「失敗した」と判断されてしまいます。
これは未実装のテストが「成功」と判断されてしまうのと同じくらいまずいことです
(訳注: レポートを見ても、そのテストがほんとうに失敗したのか、
まだ実装されていないだけなのかがわかりません)。
テストの成功を青信号、失敗を赤信号と考えるなら、
テストが未完成あるいは未実装であることを表すための黄信号が必要です。
そのような場合に使用するインターフェイスが
PHPUnit_Framework_IncompleteTest で、
これは未完成あるいは未実装のテストメソッドで発生する例外を表すものです。
このインターフェイスの標準的な実装が
PHPUnit_Framework_IncompleteTestError です。
例 10.1
では SampleTest というテストケースクラスを定義しています。
便利なメソッド markTestIncomplete()
(これは、自動的に PHPUnit_Framework_IncompleteTestError
を発生させます) をテストメソッド内でコールすることで、
このメソッドがまだ完成していないことをはっきりさせます。
例 10.1: テストに未完成の印をつける
<?php
require_once 'PHPUnit/Framework.php';
class SampleTest extends PHPUnit_Framework_TestCase
{
public function testSomething()
{
// オプション: お望みなら、ここで何かのテストをしてください。
$this->assertTrue(TRUE, 'This should already work.');
// ここで処理を止め、テストが未完成であるという印をつけます。
$this->markTestIncomplete(
'このテストは、まだ実装されていません。'
);
}
}
?>
未完成のテストは、PHPUnit のコマンドライン版テストランナーでは以下のように
I で表されます。
phpunit --verbose SampleTest
PHPUnit 3.2.10 by Sebastian Bergmann.
SampleTest
I
Time: 0 seconds
There was 1 incomplete test:
1) testSomething(SampleTest)
This test has not been implemented yet.
/home/sb/SampleTest.php:14
OK, but incomplete or skipped tests!
Tests: 1, Incomplete: 1.表 10.1 に、テストを未完成扱いにするための API を示します。
表10.1 未完成のテスト用の API
| メソッド | 意味 |
|---|---|
void markTestIncomplete() | 現在のテストを未完成扱いにします。 |
void markTestIncomplete(string $message) | 現在のテストを未完成扱いにします。それを説明する文字列として $message を使用します。 |
すべてのテストがあらゆる環境で実行できるわけではありません。 考えてみましょう。たとえば、データベースの抽象化レイヤーを使用しており、 それがさまざまなドライバを使用してさまざまなデータベースシステムを サポートしているとします。MySQL ドライバのテストができるのは、 当然 MySQL サーバが使用できる環境だけです。
例 10.2
に示すテストケースクラス DatabaseTest には、
テストメソッド testConnection() が含まれています。
このクラスのテンプレートメソッド setUp() では、
MySQLi 拡張モジュールが使用可能かを調べたうえで、もし使用できない場合は
markTestSkipped() メソッドでテストを省略するようにしています。
例 10.2: テストを省略する
<?php
require_once 'PHPUnit/Framework.php';
class DatabaseTest extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!extension_loaded('mysqli')) {
$this->markTestSkipped(
'MySQLi 拡張モジュールが使用できません。'
);
}
}
public function testConnection()
{
// ...
}
}
?>
飛ばされたテストは、PHPUnit のコマンドライン版テストランナーでは以下のように
S で表されます。
phpunit --verbose DatabaseTest
PHPUnit 3.2.10 by Sebastian Bergmann.
DatabaseTest
S
Time: 0 seconds
There was 1 skipped test:
1) testConnection(DatabaseTest)
The MySQLi extension is not available.
/home/sb/DatabaseTest.php:11
OK, but incomplete or skipped tests!
Tests: 1, Skipped: 1.表 10.2 に、テストを省略するための API を示します。
表10.2 テストを省略するための API
| メソッド | 意味 |
|---|---|
void markTestSkipped() | 現在のテストを省略扱いにします。 |
void markTestSkipped(string $message) | 現在のテストを省略扱いにします。それを説明する文字列として $message を使用します。 |
オブジェクトに対するコールが正しく行われたかどうかを調べたいこともあるでしょう。
その方法をここで説明します。ここでは、別のオブジェクトを観察している
あるオブジェクトの特定のメソッド (この例では update())
が正しくコールされたかどうかを調べるものとします。
例 11.1 では、まず
PHPUnit_Framework_TestCase クラスの
getMock() メソッド (表 21.7 を参照ください)
を使用して Observer のモックオブジェクトを作成します。
getMock() メソッドの二番目の (オプションの)
パラメータに配列を指定しているので、Observer
クラスの中の update() メソッドについてのみモック実装が作成されます。
次に、PHPUnit が提供する、いわゆる
Fluent Interface
(流れるようなインターフェイス)
を用いてモックの振る舞いや期待する動作を指定します。簡単に言うと、
いくつもの一時オブジェクト (例えば「update()
がコールされることを期待するオブジェクト」と「パラメータに
○○が指定されることを期待するオブジェクト」) を作成して、
期待値を設定したあとにそれらを連結するといった操作は必要ないということです。
その代わりに、例にあるようにメソッドの呼び出しを連結します。
このほうが、より読みやすく "流れるような" コードとなります。
例 11.1: あるメソッドが、指定したパラメータで一度だけコールされることを確かめるテスト
<?php
require_once 'PHPUnit/Framework.php';
class ObserverTest extends PHPUnit_Framework_TestCase
{
public function testUpdateIsCalledOnce()
{
// Observer クラスのモックオブジェクトを作成します。
// 対象とするのは update() メソッドのみです。
$observer = $this->getMock('Observer', array('update'));
// update() メソッドが一度だけコールされ、その際の
// パラメータは文字列 'something' となる、
// ということを期待しています。
$observer->expects($this->once())
->method('update')
->with($this->equalTo('something'));
// Subject オブジェクトを作成し、Observer オブジェクトの
// モックをアタッチします。
$subject = new Subject;
$subject->attach($observer);
// $subject オブジェクトの doSomething() メソッドをコールします。
// これは、Observer オブジェクトのモックの update() メソッドを、
// 文字列 'something' を引数としてコールすることを期待されています。
$subject->doSomething();
}
}
?>
表 11.1 は、モックメソッドの予定実行回数を表すために使用できる matcher の一覧です。
表11.1 Matchers
| Matcher | 意味 |
|---|---|
PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount any() | 評価対象のメソッドがゼロ回以上実行された際にマッチするオブジェクトを返します。 |
PHPUnit_Framework_MockObject_Matcher_InvokedCount never() | 評価対象のメソッドが実行されなかった際にマッチするオブジェクトを返します。 |
PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce atLeastOnce() | 評価対象のメソッドが最低一回以上実行された際にマッチするオブジェクトを返します。 |
PHPUnit_Framework_MockObject_Matcher_InvokedCount once() | 評価対象のメソッドが一度だけ実行された際にマッチするオブジェクトを返します。 |
PHPUnit_Framework_MockObject_Matcher_InvokedCount exactly(int $count) | 評価対象のメソッドが指定した回数だけ実行された際にマッチするオブジェクトを返します。 |
PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex at(int $index) | 評価対象のメソッドが $index 回目に実行された際にマッチするオブジェクトを返します。 |
これらの matcher とともに使用する制約については、 表 21.2 で説明しています。
あるいは、Subject の実装をテストする際に
モックオブジェクトを使用せずに 自己シャント (Self Shunt) パターン
を適用する方法もあります。
これは、テストケース自身をスタブとして使用します。
自己シャントというのは医学用語で、薬を注入する場所を確保するため、
チューブを挿入して動脈からとった血液を静脈にもどすことを意味します。
まず、Observer を実装したテストケースクラスを作成します。
Observer は、Subject
を観察したいオブジェクトが実装しなければならないインターフェイスです。
class ObserverTest extends PHPUnit_Framework_TestCase implements Observer
{
}
次に、Observer のメソッドである update()
を実装します。そして、観察対象のオブジェクトである Subject
の状態が変化した場合にそのメソッドが正しくコールされるかどうかを調べます。
public $wasCalled = FALSE;
public function update(Subject $subject)
{
$this->wasCalled = TRUE;
}
さあ、それではテストを書いてみましょう。まず新しい Subject
オブジェクトを作成し、テストオブジェクトをオブザーバとしてアタッチします。
Subject の状態が変化すると (例えば doSomething()
メソッドがコールされるなど)、Subject
オブジェクトは全オブザーバの update()
メソッドをコールしなければなりません。ここではインスタンス変数
$wasCalled を使用し、update()
の中でその値を設定します。これにより、Subject
オブジェクトが期待通りの動作をしているかどうかを確かめます。
public function testUpdate()
{
$subject = new Subject;
$subject->attach($this);
$subject->doSomething();
$this->assertTrue($this->wasCalled);
}
グローバルなインスタンスではなく、新しい Subject
オブジェクトを作成したことに注意してください。スタブを利用する際には、
このような設計をお勧めします。こうすると、
オブジェクト間の結合度が緩やかになり、再利用性が高まります。
自己シャントパターンになじみのない方には、このテストはわかりにくいかもしれません。 いったいここで何が起こっているんだ? テストケース自身がオブザーバになるだって? でも、お決まりのパターンをいったん身につけてしまえば、 これらのテストは簡単に理解できるようになります。 テストの内容をつかむために必要なことは、 すべてひとつのクラスの中に含まれているのですから。
いろいろな要因で失敗する可能性があるテストよりも、 単一の事項のみをテストするテストのほうが情報を得やすくなります。では、 できるだけ外部からの影響を受けないようなテストを作成するには どうすればいいのでしょうか? 単純なことです。 高価で面倒で頼りなくて遅くて複雑なリソースを、 テスト用に自動で生成されたスタブに置き換えればいいのです。たとえば、 複雑な計算結果を単に定数に置き換えたものを、テスト用に作成すればいいのです。
スタブを使用すると、高価な外部リソースを使用することによる問題を解決できます。 たとえばデータベース接続のようなリソースをテスト間で共有できれば便利ですが、 テストの目的を考えると、データベースを使用せずにすむのならばそのほうがずっとよいでしょう。
例 11.2 に、スタブメソッドの作成と返り値の設定の方法を示します。
例 11.2: メソッド呼び出しのスタブの作成
<?php
require_once 'PHPUnit/Framework.php';
class StubTest extends PHPUnit_Framework_TestCase
{
public function testStub()
{
$stub = $this->getMock('SomeClass', array('doSomething'));
$stub->expects($this->any())
->method('doSomething')
->will($this->returnValue('foo'));
// $stub->doSomething() をコールすると、'foo' を返します。
}
}
?>
表 11.2 は、スタブメソッドの返り値を設定する際に使用できるメソッドの一覧です。
表11.2 スタブの API
| メソッド | 意味 |
|---|---|
PHPUnit_Framework_MockObject_Stub_Exception throwException(Exception $exception) | すべてのメソッド起動時にスローされる例外を設定します。 |
PHPUnit_Framework_MockObject_Stub_Return returnValue(mixed $value) | メソッドが実行された場合の返り値を $value に設定します。 |
PHPUnit_Framework_MockObject_Stub_ConsecutiveCalls onConsecutiveCalls(mixed $value, ...) | メソッドが続けて実行された場合に返す値を設定します。 |
また、スタブを使用することで、よりよい設計を行うことができるようにもなります。
あちこちで使用されているリソースを単一の窓口 (façade : ファサード)
経由でアクセスするようにすることで、
それを簡単にスタブに置き換えられるようになります。例えば、
データベースへのアクセスのコードをそこらじゅうにちりばめるのではなく、
その代わりに IDatabase インターフェイスを実装した単一の
Database オブジェクトを使用するようにします。すると、
IDatabase を実装したスタブを作成することで、
それをテストに使用できるようになるのです。同時に、
テストを行う際にスタブデータベースを使用するか
本物のデータベースを使用するかを選択できるようになります。
つまり開発時にはローカル環境でテストし、
統合テスト時には実際のデータベースでテストするといったことができるようになるのです。
スタブ化しなければならない機能は、たいてい同一オブジェクト内で密結合しています。 この機能ををひとつの結合したインターフェイスにまとめることで、 システムのそれ以外の部分との結合を緩やかにすることができます。
You can always write more tests. However, you will quickly find that only a fraction of the tests you can imagine are actually useful. What you want is to write tests that fail even though you think they should work, or tests that succeed even though you think they should fail. Another way to think of it is in cost/benefit terms. You want to write tests that will pay you back with information. テストはいくらでも書くことができる。でも、じきにわかるだろうが、 きみが考えているテストの中で本当に有用なものはごくわずかだ。 本当に書かなきゃいけないのは、 これは動くだろうと考えているにもかかわらず失敗するテスト。それから、 これは失敗するだろうと考えているにもかかわらず実際は成功するテストだ。 あるいはコストと利益の観点から考えてみてもいいだろう。 きみに何らかの情報を返してくれるテストを書かないとね。 | ||
| --Erich Gamma | ||
開発中のソフトウェアの内部構造を変更し、 わかりやすく変更が簡単なものにする必要が出てきたときのことを考えましょう。 それによってソフトウェアの外部的な振る舞いが変わってしまってはいけません。 この、いわゆる リファクタリング (日本語) を安全に行うにあたり、テストスイートが非常に重要となります。 もしテストスイートがなければ、リファクタリングによってシステムを壊してしまっても あなたはそれに気づかないでしょう。
以下の条件が、あなたのプロジェクトのコードや設計を改善するための助けとなるでしょう。 また、単体テストを使用することで、リファクタリングによって振る舞いが変化していないこと・ エラーが発生していないことが確認できます。
すべての単体テストが正常に動作すること。
コードが設計指針を満たしていること。
コードに冗長性がないこと。
コードには最小限のクラスおよびメソッドのみが含まれていること。
システムに新しい機能を追加する際には、まず最初にテストを書きます。 そのテストがきちんと実行できるようになった時点で、開発は終了です。 この手法については、次の章で詳しく説明します。
不具合の報告を受けたら、すぐにでもそれを修正したいと思われることでしょう。 しかし、あせって修正しようとしても、経験上なかなかうまくいきません。 不具合を修正したつもりが新たな不具合を引き起こしていたなんてこともありがちですね。
はやる気持ちを抑えて、以下のようにしてみましょう。
不具合を再現できることを確認します。
不具合が発生する最小限のコードを見つけます。例えば、 もしおかしな数値が出力されるのなら、 その数値を計算しているオブジェクトが何なのかを探します。
その不具合のせいで今は失敗する (そして、不具合が修正されたら成功する) テストを書きます。
不具合を修正します。
不具合が再現する最小限のコードを見つける過程で、 不具合の原因がわかるかもしれません。テストを書くことによって、 不具合を真の意味で修正できる可能性が高まるでしょう。なぜなら、 テストを書くことで、将来同じ間違いをする可能性を減らせるからです。 これまでに書いたすべてのテストが、 不注意によって別の問題を発生させる可能性を減らすために役立っているのです。
Unit testing offers many advantages:
Overall, integrated unit testing makes the cost and risk of any individual change smaller. It will allow the project to make [...] major architectural improvements [...] quickly and confidently. 単体テストには、こんなに多くの利点がある。
まとめよう。単体テストをうまく組み込めば、 プログラムを変更する際の手間やリスクをより減らすことになるのだ。 It will allow the project to make [...] major architectural improvements [...] quickly and confidently. | ||
| --Benjamin Smedberg | ||
テストファーストプログラミング、 エクストリームプログラミング、 そして テスト駆動開発 などのソフトウェア開発方法論において、単体テストは非常に重要な位置を占めています。 また、構造上この手法に対応できない言語については 規約による設計 (Design-by-Contract) という手法も認めています。
プログラムを書き終えてから PHPUnit でテストを書くこともできます。 が、テストを書き始めるのが早ければ早いほど、テストの価値が高くなります。 コードが「完成」して何ヶ月もたってからテストを書き始めるのではなく、数日後、 数時間後、いやもうひとがんばりして数分後に書き始めることだってできるでしょう。 さらにもう一歩先へ進んでみませんか? コードを書き始める前にテストを書いたっていいんじゃないですか?
エクストリームプログラミングやテスト駆動開発における 「テストファーストプログラミング」はこの考えに基づいたもので、 さらにそれを究極まで推し進めたものです。現在のコンピュータの能力をもってすれば、 一日に何千ものテストを何千回も繰り返すことだって可能です。 これらのテスト結果を活用することで、 プログラムを少しずつ確実に作成することができるようになります。 テストを自動化すると、新しく追加したテストだけでなく これまでのテストもすべて実行できることが保証されるのです。 テストとはハーケン (登山のときにザイルを通したりする頭部に穴の開いた鋼鉄製の釘) のようなもので、何が起ころうともこの段階までは確実に完成しているということを保証してくれます。
最初にテストを書き始めたときは、おそらくそれを実行できないでしょう。 だって、まだ実装していないオブジェクトやメソッドを使用しているのだから。 最初のうちはこれを気持ち悪く感じるかもしれません。でもそのうちに慣れてきます。 テストファーストプログラミングというのは、オブジェクト指向開発の原則である 「実装をプログラミングするのではなくインターフェイスをプログラミングする」 に従うための実践的な手法であると考えましょう。テストを書いている間、 あなたはきっとテスト対象オブジェクトのインターフェイス (このオブジェクトは、 外部からはどのように見えるのか) について考えていることでしょう。 テストが実際に動作するようになったら、そこで実装のことを考え始めます。 出来上がったテストによって、この段階でインターフェイスは確定しています。
The point of Test-Driven Development is to drive out the functionality the software actually needs, rather than what the programmer thinks it probably ought to have. The way it does this seems at first counterintuitive, if not downright silly, but it not only makes sense, it also quickly becomes a natural and elegant way to develop software. テスト駆動開発 (日本語) のポイントは、プログラマが「こうあるべき」と考える機能ではなく そのソフトウェアが実際に必要としている機能を作り出すことだ。 これは、最初のうちは直感に反するばかばかしいことだと感じるかもしれない。 しかし、これは合理的なものであり、近いうちに 自然でエレガントなソフトウェア開発手法となるだろう。 | ||
| --Dan North | ||
この後に続くテストファーストプログラミングの例は、やむを得ず簡潔なものになっています。 詳細については Kent Beck の Test-Driven Development [Beck2002] [Beck2002-ja] や Dave Astels の A Practical Guide to Test-Driven Development [Astels2003] などの書籍を参照ください。
この章では、銀行口座を表すクラスを例にして考えます。預金残高の取得や設定、
預け入れや引き落としなどのメソッドだけでなく、BankAccount
クラスは以下のふたつの規約を満たす必要があります。
預金残高の初期値はゼロでなければならない。
預金残高がゼロ未満になってはならない。
テストファーストプログラミングの手法に従い、まずは BankAccount
クラスのテストを作成し、その後で実際のコードを書いていくようにしましょう。
上の規約をテスト作成の基準とし、それにしたがって
例 13.1
のようにテストメソッドの名前をつけます。
例 13.1: BankAccount クラスのテスト
<?php
require_once 'PHPUnit/Framework.php';
require_once 'BankAccount.php';
class BankAccountTest extends PHPUnit_Framework_TestCase
{
protected $ba;
protected function setUp()
{
$this->ba = new BankAccount;
}
public function testBalanceIsInitiallyZero()
{
$this->assertEquals(0, $this->ba->getBalance());
}
public function testBalanceCannotBecomeNegative()
{
try {
$this->ba->withdrawMoney(1);
}
catch (BankAccountException $e) {
$this->assertEquals(0, $this->ba->getBalance());
return;
}
$this->fail();
}
public function testBalanceCannotBecomeNegative2()
{
try {
$this->ba->depositMoney(-1);
}
catch (BankAccountException $e) {
$this->assertEquals(0, $this->ba->getBalance());
return;
}
$this->fail();
}
}
?>
それでは、最初のテスト testBalanceIsInitiallyZero()
をクリアするために必要な最小限のコードを書いていきましょう。必要なのは、
BankAccount クラスの getBalance()
メソッドを
例 13.2
のように実装することです。
例 13.2: テスト testBalanceIsInitiallyZero() をクリアするために必要なコード
<?php
class BankAccount
{
protected $balance = 0;
public function getBalance()
{
return $this->balance;
}
}
?>
これで最初のテストはクリアすることになりましたが、 2 番目のテストには失敗します。なぜなら、 テストメソッド内でコールしているメソッドがまだ実装されていないからです。
phpunit BankAccountTest
PHPUnit 3.2.10 by Sebastian Bergmann.
.
Fatal error: Call to undefined method BankAccount::withdrawMoney()
ふたつめの規約のテストをクリアするには、withdrawMoney()、
depositMoney() および setBalance()
の各メソッドを
例 13.3
のように実装しなければなりません。これらのメソッドは、
規約に反するような引数でコールされた場合には BankAccountException
を発生させるように実装しています。
例 13.3: 完全な BankAccount クラス
<?php
class BankAccount
{
protected $balance = 0;
public function getBalance()
{
return $this->balance;
}
protected function setBalance($balance)
{
if ($balance >= 0) {
$this->balance = $balance;
} else {
throw new BankAccountException;
}
}
public function depositMoney($balance)
{
$this->setBalance($this->getBalance() + $balance);
return $this->getBalance();
}
public function withdrawMoney($balance)
{
$this->setBalance($this->getBalance() - $balance);
return $this->getBalance();
}
}
?>
これで、2 つめの規約に関するテストにもクリアするようになります。
phpunit BankAccountTest
PHPUnit 3.2.10 by Sebastian Bergmann.
...
Time: 0 seconds
OK (3 tests)
別の方法としては、PHPUnit_Framework_Assert
クラスが提供する静的なアサーションメソッドを用いて、コード内に
「規約による設計」方式のアサーションを記述するというものもあります。
例 13.4
がその例です。これらのアサーションのいずれかに失敗すると、例外
PHPUnit_Framework_AssertionFailedError が発生します。
この方式を用いると、条件チェックのコードを減らすことができてテストが読みやすくなります。
ただ、プログラムの実行時にも PHPUnit が必要になってしまいます。
例 13.4: 「規約による設計」のアサーションを使用した BankAccount クラス
<?php
require_once 'PHPUnit/Framework.php';
class BankAccount
{
private $balance = 0;
public function getBalance()
{
return $this->balance;
}
protected function setBalance($balance)
{
PHPUnit_Framework_Assert::assertTrue($balance >= 0);
$this->balance = $balance;
}
public function depositMoney($amount)
{
PHPUnit_Framework_Assert::assertTrue($amount >= 0);
$this->setBalance($this->getBalance() + $amount);
return $this->getBalance();
}
public function withdrawMoney($amount)
{
PHPUnit_Framework_Assert::assertTrue($amount >= 0);
PHPUnit_Framework_Assert::assertTrue($this->balance >= $amount);
$this->setBalance($this->getBalance() - $amount);
return $this->getBalance();
}
}
?>
規約を満たすための条件をテスト内に記述することで、「規約による設計」
方式で BankAccount クラスをプログラミングしてきました。
次に、テストファーストプログラミングの考え方にしたがって、
テストをクリアするために必要なコードを記述してきました。
でも、ひとつ忘れてしまったことがあります。それは、
setBalance()、depositMoney()
および withdrawMoney() に正当な値を指定した場合に、
正常に動作することを確かめるテストを書くことです。
自分が書いたテストが妥当なものなのか、
それで十分なのかを調べるためのテストが必要ですね。次の章では、そのための
「コードカバレッジ解析」について説明します。
ユニットテストでコードをテストする方法はわかりました。でも、 テストそのものをテストするにはどうしたらいいのでしょう? テストされていないコードを見つけるには? 言い換えれば、まだテストで カバーされていない部分を見つけるには? 完全にテストができたことをどうやって確認するの? これらのすべての疑問に対する答えとなるのが、コードカバレッジ解析という手法です。 コードカバレッジ解析を行うと、 コードのどの部分がテストされたのかを調べることができるようになります。
PHPUnit のコードカバレッジ解析では、Xdebug 拡張モジュールが提供するステートメントカバレッジ機能を利用しています。 ステートメントカバレッジというのは、たとえば 100 行のコードで構成されるメソッドがあった場合に、 もしテストで実際に実行されたのがそのうちの 75 行だけだったなら、 そのメソッドのコードカバレッジは 75 パーセントだと考えるということです。
それでは、例 13.3
の BankAccount クラスについての
コードカバレッジレポートを作成してみましょう。
phpunit --coverage-html ./report BankAccountTest
PHPUnit 3.2.10 by Sebastian Bergmann.
...
Time: 0 seconds
OK (3 tests)
Generating report, this may take a moment.図 14.1 は、コードカバレッジレポートの一部を抜粋したものです。 テスト時に実行された行は、緑色で強調表示されます。 実行可能なコードであるにもかかわらず実行されなかった行については赤色で強調表示されます。 また、"無意味なコード" についてはオレンジ色で強調表示されます。 行の左にある数字は、その行をカバーするテストの数を表します。
BankAccount のコードカバレッジレポートからわかることは、
setBalance()、depositMoney()
をコールするテストがまだ存在しないということ、
そして withdrawMoney()
に正しい値を指定した場合のテストも存在しないということです。
BankAccountTest クラスに追加するテストを
例 14.1
に示します。これによって、BankAccount
クラスのテストケースを完全に網羅できるようになります。
例 14.1: 完全なコードカバレッジを達成するために欠けているテスト
<?php
require_once 'PHPUnit/Framework.php';
require_once 'BankAccount.php';
class BankAccountTest extends PHPUnit_Framework_TestCase
{
// ...
public function testDepositWithdrawMoney()
{
$this->assertEquals(0, $this->ba->getBalance());
$this->ba->depositMoney(1);
$this->assertEquals(1, $this->ba->getBalance());
$this->ba->withdrawMoney(1);
$this->assertEquals(0, $this->ba->getBalance());
}
}
?>
図 14.2 は、
テストを追加した後の setBalance() のコードカバレッジです。
テストコードで @covers アノテーションを使用すると、
そのテストメソッドがどのメソッドをテストしたいのかを指定することができます。
これを指定すると、指定したメソッドのコードカバレッジ情報のみを考慮します。
例 14.2
に例を示します。
例 14.2: どのメソッドを対象とするかを指定したテスト
<?php
require_once 'PHPUnit/Framework.php';
require_once 'BankAccount.php';
class BankAccountTest extends PHPUnit_Framework_TestCase
{
protected $ba;
protected function setUp()
{
$this->ba = new BankAccount;
}
/**
* @covers BankAccount::getBalance
*/
public function testBalanceIsInitiallyZero()
{
$this->assertEquals(0, $this->ba->getBalance());
}
/**
* @covers BankAccount::withdrawMoney
*/
public function testBalanceCannotBecomeNegative()
{
try {
$this->ba->withdrawMoney(1);
}
catch (BankAccountException $e) {
$this->assertEquals(0, $this->ba->getBalance());
return;
}
$this->fail();
}
/**
* @covers BankAccount::depositMoney
*/
public function testBalanceCannotBecomeNegative2()
{
try {
$this->ba->depositMoney(-1);
}
catch (BankAccountException $e) {
$this->assertEquals(0, $this->ba->getBalance());
return;
}
$this->fail();
}
/**
* @covers BankAccount::getBalance
* @covers BankAccount::depositMoney
* @covers BankAccount::withdrawMoney
*/
public function testDepositWithdrawMoney()
{
$this->assertEquals(0, $this->ba->getBalance());
$this->ba->depositMoney(1);
$this->assertEquals(1, $this->ba->getBalance());
$this->ba->withdrawMoney(1);
$this->assertEquals(0, $this->ba->getBalance());
}
}
?>
どうしてもテストができないコードブロックなどを、
コードカバレッジ解析時に無視させたいこともあるでしょう。
PHPUnit でこれを実現するには、
@codeCoverageIgnoreStart および
@codeCoverageIgnoreEnd アノテーションを
例 14.3
のように使用します。
例 14.3: @codeCoverageIgnoreStart および @codeCoverageIgnoreEnd アノテーションの使用法
<?php
class Sample
{
// ...
public function doSomething()
{
if (0) {
// @codeCoverageIgnoreStart
$this->doSomethingElse();
// @codeCoverageIgnoreEnd
}
}
// ...
}
?>
@codeCoverageIgnoreStart アノテーションから
@codeCoverageIgnoreEnd アノテーションまでの間の行は、
(たとえ実行されなかったとしても) 実行されたものとみなされ、
強調表示されません。
デフォルトでは、1 行でもコードが実行されたソースコードファイルはすべて
(そしてそのようなファイルのみが) レポートに含められます。
レポートにどのようなソースコードファイルを含めるかを設定するには
PHPUnit_Util_Filter API を使用します
(表 14.1
を参照ください)。
表14.1 PHPUnit_Util_Filter の API
| メソッド | 意味 |
|---|---|
void addDirectoryToFilter(string $directory) | あるディレクトリ内でファイル名の最後が .php であるすべてのファイルを (再帰的に) ブラックリストに追加します。 |
void addDirectoryToFilter(string $directory, string $suffix) | あるディレクトリ内でファイル名の最後が $suffix であるすべてのファイルを (再帰的に) ブラックリストに追加します。 |
void addFileToFilter(string $filename) | ファイルをブラックリストに追加します。 |
void removeDirectoryFromFilter(string $directory) | あるディレクトリ内でファイル名の最後が .php であるすべてのファイルを (再帰的に) ブラックリストから削除します。 |
void removeDirectoryFromFilter(string $directory, string $suffix) | あるディレクトリ内でファイル名の最後が $suffix であるすべてのファイルを (再帰的に) ブラックリストから削除します。 |
void removeFileFromFilter(string $filename) | ファイルをブラックリストから削除します。 |
void addDirectoryToWhitelist(string $directory) | あるディレクトリ内でファイル名の最後が .php であるすべてのファイルを (再帰的に) ホワイトリストに追加します。 |
void addDirectoryToWhitelist(string $directory, string $suffix) | あるディレクトリ内でファイル名の最後が $suffix であるすべてのファイルを (再帰的に) ホワイトリストに追加します。 |
void addFileToWhitelist(string $filename) | ファイルをホワイトリストに追加します。 |
void removeDirectoryFromWhitelist(string $directory) | あるディレクトリ内でファイル名の最後が .php であるすべてのファイルを (再帰的に) ホワイトリストから削除します。 |
void removeDirectoryFromWhitelist(string $directory, string $suffix) | あるディレクトリ内でファイル名の最後が $suffix であるすべてのファイルを (再帰的に) ホワイトリストから削除します。 |
void removeFileFromWhitelist(string $filename) | ファイルをホワイトリストから削除します。 |
ブラックリストには、PHPUnit 自身のソースコードファイルやテストファイルがデフォルトで登録されています。 ホワイトリストが空 (デフォルト) の場合はブラックリストを使用し、 ホワイトリストが空でない場合はホワイトリストを使用します。 ホワイトリストを使用する場合は、そのファイルが実行されるかどうかにかかわらず リスト内のすべてのファイルがコードカバレッジレポートに追加されます。
自動テストに慣れてくると、 ほかの目的のためにもテストを使いたくなってくることでしょう。 ここではそんな例を説明します。
一般的に、エクストリームプログラミングのようなアジャイルプロセスを採用しているプロジェクトでは、 ドキュメントの内容が実際の設計やコードに追いついていないことが多いものです。 エクストリームプログラミングでは コードの共同所有 (collective code ownership) を要求しており、 すべての開発者がシステム全体の動作を知っておく必要があります。 作成するテストに対して、そのクラスが何を行うべきなのかを示すような 「わかりやすい」名前をつけられるようにさえしておけば、PHPUnit の TestDox 機能を使用して自動的にドキュメントを生成することができます。 このドキュメントにより、開発者たちはプロジェクト内の各クラスが どのようにふるまうべきなのかを知ることができます。
PHPUnit の TestDox 機能は、テストクラス内のすべてのテストメソッドの名前を抽出し、
それを PHP 風のキャメルケースから通常の文に変換します。つまり
testBalanceIsInitiallyZero() が "Balance is initially zero"
のようになるわけです。最後のほうの数字のみが違うメソッド、例えば
testBalanceCannotBecomeNegative() と
testBalanceCannotBecomeNegative2() のようなものが存在した場合は、
文 "Balance cannot become negative" は一度のみ表示され、
全てのテストが成功したことを表します。
BankAccount クラスのアジャイルな文書
(例 13.1
を参照ください) を見てみましょう。
phpunit --testdox BankAccountTest
PHPUnit 3.2.10 by Sebastian Bergmann.
BankAccount
- Balance is initially zero
- Balance cannot become negative
また、アジャイルな文書を HTML あるいはプレーンテキスト形式で作成してファイルに書き出すこともできます。
この場合は、引数 --testdox-html
あるいは --testdox-text を使用します。
アジャイルな文書は、プロジェクト内であなたが作成しようとしている外部パッケージについて、 このように動作するであるという期待をまとめた文書にもなります。 外部のパッケージを使用するときには、 そのパッケージが期待通りに動作しなくなるというリスクに常にさらされています。 パッケージのバージョンアップにより知らないうちに挙動が変わってしまい、 あなたのコードが動作しなくなる可能性もあります。そのようなことを避けるため、 「このパッケージはこのように動作するはず」 ということを常にテストケースで記述しておくようにします。テストが成功すれば、 期待通りに動作していることがわかります。もし動作仕様をすべてテストで記述できているのなら、 外部パッケージが将来バージョンアップされたとしても何の心配もいりません。 テストをクリアしたということは、システムは期待通りに動作するということだからです。
あるパッケージについての機能を文書化するためにテストを書いているとき、 そのテストの所有者はあなたです。今あなたがテストを作成しているパッケージの作者は、 そのテストのことについては何も知りません。パッケージの作者とよりつながりを深めるため、 作成したテストを使用してコミュニケートしたり、 そのテストを使用して共同作業をしたりすることができるでしょう。
あなたが作成したテストを使用してパッケージの作者と共同作業をすることになれば、 テストも共同で書くことになります。そうすることで、 より多くのテストケースを挙げられるようになるでしょう。 「暗黙の了解」などに頼っていては、共同作業はできません。 テストと同時に、あなたはそのパッケージに対して期待していることを正確に文書化することになります。 また、すべてのテストにクリアした時点で、 作者はパッケージが完成したことを知ることになります。
スタブ (本書の前のほうで説明した "モックオブジェクト" の章を参照ください) を使用することで、パッケージの作者と別れても作業できるようになります。 パッケージ作者の仕事は、パッケージの実際の実装でテストをクリアするようにすること。 そしてあなたの仕事はあなたが書いたコードでテストをクリアするようにすることです。 この段階になれば、あなたはスタブオブジェクトを使用すればよいのです。 このやり方により、2 つのチームが独立して開発できるようになります。
PHPUnit は、テスト結果の出力書式として以下のようなものに対応しています。
PHPUnit がサポートする XML のフォーマットは、
Apache Ant の JUnit タスク
が使用しているものを参考にしています。
以下の例は、ArrayTest のテストが生成した
XML ログファイルです。
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="ArrayTest"
file="/home/sb/ArrayTest.php"
tests="2"
failures="0"
errors="0"
time="0.016030">
<testcase name="testNewArrayIsEmpty"
class="ArrayTest"
file="/home/sb/ArrayTest.php"
line="6"
time="0.008044"/>
<testcase name="testArrayContainsAnElement"
class="ArrayTest"
file="/home/sb/ArrayTest.php"
line="15"
time="0.007986"/>
</testsuite>
</testsuites>
次の XML ログファイルは、テストクラス
FailureErrorTest にある 2 つのテスト
testFailure および testError
が出力したものです。失敗やエラーがどのように表示されるのかを確認しましょう。
<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
<testsuite name="FailureErrorTest"
file="/home/sb/FailureErrorTest.php"
tests="2"
failures="1"
errors="1"
time="0.019744">
<testcase name="testFailure"
class="FailureErrorTest"
file="/home/sb/FailureErrorTest.php"
line="6"
time="0.011456">
<failure type="PHPUnit_Framework_ExpectationFailedException">
testFailure(FailureErrorTest)
Failed asserting that <integer:2> matches expected value <integer:1>.
/home/sb/FailureErrorTest.php:8
</failure>
</testcase>
<testcase name="testError"
class="FailureErrorTest"
file="/home/sb/FailureErrorTest.php"
line="11"
time="0.008288">
<error type="Exception">testError(FailureErrorTest)
Exception:
/home/sb/FailureErrorTest.php:13
</error>
</testcase>
</testsuite>
</testsuites>
PHPUnit がコードカバレッジ情報のログ出力の際に使用している XML のフォーマットは、
Clover
のものを参考にしています。
以下の例は、BankAccountTest のテストが生成した
XML ログファイルです。
<?xml version="1.0" encoding="UTF-8"?>
<coverage generated="1184835473" phpunit="3.2.10">
<project name="BankAccountTest" timestamp="1184835473">
<file name="/home/sb/BankAccount.php">
<class name="BankAccountException">
<metrics methods="0" coveredmethods="0" statements="0"
coveredstatements="0" elements="0" coveredelements="0"/>
</class>
<class name="BankAccount">
<metrics methods="4" coveredmethods="4" statements="13"
coveredstatements="5" elements="17" coveredelements="9"/>
</class>
<line num="77" type="method" count="3"/>
<line num="79" type="stmt" count="3"/>
<line num="89" type="method" count="2"/>
<line num="91" type="stmt" count="2"/>
<line num="92" type="stmt" count="0"/>
<line num="93" type="stmt" count="0"/>
<line num="94" type="stmt" count="2"/>
<line num="96" type="stmt" count="0"/>
<line num="105" type="method" count="1"/>
<line num="107" type="stmt" count="1"/>
<line num="109" type="stmt" count="0"/>
<line num="119" type="method" count="1"/>
<line num="121" type="stmt" count="1"/>
<line num="123" type="stmt" count="0"/>
<metrics loc="126" ncloc="37" classes="2" methods="4" coveredmethods="4"
statements="13" coveredstatements="5" elements="17"
coveredelements="9"/>
</file>
<metrics files="1" loc="126" ncloc="37" classes="2" methods="4"
coveredmethods="4" statements="13" coveredstatements="5"
elements="17" coveredelements="9"/>
</project>
</coverage>
JavaScript Object Notation (JSON)
は、軽量なデータ交換用フォーマットです。次の例は、
ArrayTest のテストが作成した JSON メッセージです。
{"event":"suiteStart","suite":"ArrayTest","tests":2}
{"event":"test","suite":"ArrayTest",
"test":"testNewArrayIsEmpty(ArrayTest)","status":"pass",
"time":0.000460147858,"trace":[],"message":""}
{"event":"test","suite":"ArrayTest",
"test":"testArrayContainsAnElement(ArrayTest)","status":"pass",
"time":0.000422954559,"trace":[],"message":""}
次の JSON メッセージは、
FailureErrorTest にある 2 つのテスト
testFailure および testError
が出力したものです。失敗やエラーがどのように表示されるのかを確認しましょう。
{"event":"suiteStart","suite":"FailureErrorTest","tests":2}
{"event":"test","suite":"FailureErrorTest",
"test":"testFailure(FailureErrorTest)","status":"fail",
"time":0.000483989716,"trace":[],"message":""}
{"event":"test","suite":"FailureErrorTest",
"test":"testError(FailureErrorTest)","status":"error",
"time":0.000466108322,"trace":[],"message":""}
Test Anything Protocol (TAP)
は、Perl のモジュールをテストする際に使用する、
シンプルなテキストベースのインターフェイスです。
以下の例は、ArrayTest のテストが生成した
TAP ログファイルです。
1..2 # TestSuite "ArrayTest" started. ok 1 - testNewArrayIsEmpty(ArrayTest) ok 2 - testArrayContainsAnElement(ArrayTest) # TestSuite "ArrayTest" ended.
次の TAP ログファイルは、テストクラス
FailureErrorTest にあるメソッド
testFailure および testError
が出力したものです。失敗やエラーがどのように表示されるのかを確認しましょう。
1..2 # TestSuite "FailureErrorTest" started. not ok 1 - Failure: testFailure(FailureErrorTest) not ok 2 - Error: testError(FailureErrorTest) # TestSuite "FailureErrorTest" ended.
PHPUnit は、テスト結果を図示することができます。 GraphViz ツール群を使用すると、これを画像などの便利な形式に変換することができます。
phpunit --log-graphviz BankAccount.dot BankAccountTest
PHPUnit 3.2.10 by Sebastian Bergmann.
...
Time: 0 seconds
OK (3 tests)
次の例は、BankAccountTest のテストから生成された
(そしてカレントディレクトリの BankAccount.dot
に保存された) GraphViz マークアップです。
digraph G {
graph [ overlap="scale",splines="true",sep=".1",fontsize="8" ];
"BankAccountTest" [ color="green" ];
subgraph "cluster_BankAccountTest" {
label="";
"testBalanceIsInitiallyZero" [ color="green" ];
"testBalanceCannotBecomeNegative" [ color="green" ];
"testBalanceCannotBecomeNegative2" [ color="green" ];
}
"BankAccountTest" -> "testBalanceIsInitiallyZero";
"BankAccountTest" -> "testBalanceCannotBecomeNegative";
"BankAccountTest" -> "testBalanceCannotBecomeNegative2";
}
GraphViz ソフトウェア群に含まれるコマンドラインツール
dot を使用すると、
このマークアップから図を作成することができます。
dot -T png -o BankAccount.png BankAccount.dot図 16.1 は、上の GraphViz マークアップをもとに作成したテスト結果の図です。
成功したテストは緑の線で、失敗したりエラーが発生したものは赤い線で、 そして不完全だったり省略されたりしたテストは黄色い線で表示されます。 テストスイートなどの親ノードは、もしその子ノード (テスト) が成功しなかった場合は緑の線になりません。
PHPUnit は、テストの結果やコードカバレッジのデータを テストデータベース に書き込むことができます。今後、このデータを利用した 新機能 を追加することを検討中です。
テストスイートを 実行 するたびに、run テーブルに行が追加されます。
テストスイート内の テスト が実行されるたびに、test テーブルに行が追加されます。
あるリビジョン内の各 ファイル について、code_file テーブルの中に対応する行が作成されます。
あるリビジョン内のファイルで宣言されている各 クラス について、code_class テーブルの中に対応する行が作成されます。
あるリビジョン内のファイルで宣言されている各 メソッド について、code_method テーブルの中に対応する行が作成されます。
あるリビジョン内のファイルの コードの各行 について、code_line テーブルの中に対応する行が作成されます。
code_coverage テーブルは、テストとそれがカバーするコードの行を関連づけます。
テストの結果やコードカバレッジをデータベースに書き込むには、 用意されているスキーマのいずれかを使用してまず最初にデータベースを作成しなければなりません。
sqlite3 BankAccount.db < PHPUnit/Util/Log/Database/SQLite3.sqlそしてテストスイートを実行し、 テストの結果とコードカバレッジデータをデータベースに書き込みます。
phpunit --test-db-dsn sqlite:///home/sb/BankAccount.db --test-db-log-rev 1 BankAccountTest
PHPUnit 3.2.10 by Sebastian Bergmann.
...
Time: 0 seconds
OK (3 tests)
Storing code coverage data in database, this may take a moment.表 16.1 は、 TextUI テストランナー (第 5 章 を参照ください) でテストデータベース用に指定できる引数をまとめたものです。
表16.1 TextUI のテストデータベース用の引数
| 引数 | 意味 |
|---|---|
--test-db-dsn <dsn> | データベースに接続するための PDO データソース名 (DSN)。 DSN は一般的な形式は、まず PDO ドライバ名、そしてコロン、 その後に各 PDO ドライバ固有の接続書式が続きます。 |
--test-db-log-rev <r> | たとえば Subversion のグローバルリビジョン番号 のような数値で、コードベースの現在のリビジョンを識別するために使用します。 |
--test-db-log-info ... | テスト環境に関する追加情報。 |
既存のコードのテストを記述する際は、 以下のようなコードを何度となく繰り返し記述することになるでしょう。
public function testMethod()
{
}PHPUnit は、既存のクラスのコードを解析して テストケースクラスの雛形を作成することができます。
次の例は、Calculator という名前のクラス
(例 17.1 を参照ください)
用のテストクラスの雛形を作成する手順を示すものです。
phpunit --skeleton Calculator
PHPUnit 3.2.10 by Sebastian Bergmann.
Wrote test class skeleton for Calculator to CalculatorTest.php.もとのクラスの各メソッドについて、 出来上がったテストケースクラスのテストケースは不完全な状態 (第 10 章 を参照ください) です。
作成されたテストケースクラスを実行した結果を以下に示します。
phpunit --verbose CalculatorTest
PHPUnit 3.2.10 by Sebastian Bergmann.
CalculatorTest
I
Time: 0 seconds
There was 1 incomplete test:
1) testAdd(CalculatorTest)
This test has not been implemented yet.
/home/sb/CalculatorTest.php:54
OK, but incomplete or skipped tests!
Tests: 1, Incomplete: 1.
@assert アノテーションを
メソッドのコメント部で使用すると、
シンプルではあるけれど意味のあるテストを自動的に生成することができます。
これは不完全なテストケースではありません。
例 17.2
に例を示します。
例 17.2: @assert アノテーションをつけた Calculator クラス
<?php
class Calculator
{
/**
* @assert (0, 0) == 0
* @assert (0, 1) == 1
* @assert (1, 0) == 1
* @assert (1, 1) == 2
*/
public function add($a, $b)
{
return $a + $b;
}
}
?>
もとのクラスの各メソッドについて、
@assert アノテーションの内容をチェックします。
これらは、以下のようなテストコードに変換されます。
/**
* Generated from @assert (0, 0) == 0.
*/
public function testAdd() {
$o = new Calculator;
$this->assertEquals(0, $o->add(0, 0));
}
作成されたテストケースクラスを実行した結果を以下に示します。
phpunit CalculatorTest
PHPUnit 3.2.10 by Sebastian Bergmann.
....
Time: 0 seconds
OK (4 tests)
表 17.1
に、サポートされる @assert
の種類と、それがどのようなテストコードに変換されるかをまとめました。
表17.1 サポートされる @assert アノテーション
| アノテーション | 変換後の内容 |
|---|---|
@assert (...) == X | assertEquals(X, method(...)) |
@assert (...) != X | assertNotEquals(X, method(...)) |
@assert (...) === X | assertSame(X, method(...)) |
@assert (...) !== X | assertNotSame(X, method(...)) |
@assert (...) > X | assertGreaterThan(X, method(...)) |
@assert (...) >= X | assertGreaterThanOrEqual(X, method(...)) |
@assert (...) < X | assertLessThan(X, method(...)) |
@assert (...) <= X | assertLessThanOrEqual(X, method(...)) |
@assert (...) throws X | @expectedException X |
Selenium RC はテストツールのひとつです。これを使用すると、 ウェブアプリケーションのユーザインターフェイスについてのテストを自動化することができます。 あらゆるプログラミング言語で稼動しているウェブサイトに対応しており、 現在主流のあらゆるブラウザで使用することができます。Selenium RC は Selenium Core を使用しています。これは、ブラウザ上でのタスクを自動的に実行する JavaScript のライブラリです。Selenium でのテストは、 一般のユーザが使用するのと同じようにブラウザ上で直接実行されます。 主な使用例としては、受け入れテスト (各システム単体のテストではなく、結合されたシステム全体に対するテスト) や ブラウザの互換性のテスト (ウェブアプリケーションを、さまざまなオペレーティングシステムやブラウザでテストする) などがあります。
Selenium RC のインストール手順は、次のようになります。
server/selenium-server.jar を /usr/local/bin などにコピーする。java -jar /usr/local/bin/selenium-server.jar などのようにして Selenium RC サーバを起動する。これで、クライアント/サーバ プロトコルを用いて Selenium RC サーバにコマンドを送信できるようになりました。
PHPUnit_Extensions_SeleniumTestCase
は、Selenium RC と通信するための クライアント/サーバ プロトコルを実装したものです。
また、ウェブのテスト用に特化したアサーションメソッドも提供します。
例 18.1 は、
ウェブサイト http://www.example.com/
の <title> 要素の内容をテストする方法を示したものです。
例 18.1: PHPUnit_Extensions_SeleniumTestCase の使用例
<?php
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
class WebTest extends PHPUnit_Extensions_SeleniumTestCase
{
protected function setUp()
{
$this->setBrowser('*firefox /usr/lib/firefox/firefox-bin');
$this->setBrowserUrl('http://www.example.com/');
}
public function testTitle()
{
$this->open('http://www.example.com/');
$this->assertTitleEquals('Example Web Page');
}
}
?>
PHPUnit_Framework_TestCase クラスとは異なり、
PHPUnit_Extensions_SeleniumTestCase を継承したテストケースクラスは
setUp() メソッドが必須となります。
このメソッド内で、Selenium RC セッションの設定を行います。
ここで使用できるメソッドの一覧は
表 18.1
を参照ください。
表18.1 Selenium RC API: セットアップ
| メソッド | 意味 |
|---|---|
void setBrowser(string $browser) | Selenium RC サーバが使用するブラウザを設定します。 |
void setBrowserUrl(string $browserUrl) | テストするベース URL を設定します。 |
void setHost(string $host) | Selenium RC サーバに接続する際のホスト名を設定します。 |
void setPort(int $port) | Selenium RC サーバに接続する際のポートを設定します。 |
void setTimeout(int $timeout) | Selenium RC サーバに接続する際のタイムアウト値を設定します。 |
void setSleep(int $seconds) | Selenium RC クライアントが、Selenium RC サーバにアクションコマンドを送信してから待機する秒数を設定します。 |
複数のブラウザを使用してテストを行なうこともできます。この場合は、
setBrowser() でブラウザの設定を行うかわりに、
テストケースクラスの中で $browsers という名前の
public static な配列を作成します。
この配列の各項目が個々のブラウザの設定を表します。
これらのブラウザは、それぞれ別の Selenium RC サーバで管理することができます。
例 18.2: 複数のブラウザの設定管理
<?php
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
class WebTest extends PHPUnit_Extensions_SeleniumTestCase
{
public static $browsers = array(
array(
'name' => 'Firefox on Linux',
'browser' => '*firefox /usr/lib/firefox/firefox-bin',
'host' => 'my.linux.box',
'port' => 4444,
'timeout' => 30000,
),
array(
'name' => 'Safari on MacOS X',
'browser' => '*safari',
'host' => 'my.macosx.box',
'port' => 4444,
'timeout' => 30000,
),
array(
'name' => 'Safari on Windows XP',
'browser' => '*custom C:\Program Files\Safari\Safari.exe -url',
'host' => 'my.windowsxp.box',
'port' => 4444,
'timeout' => 30000,
),
array(
'name' => 'Internet Explorer on Windows XP',
'browser' => '*iexplore',
'host' => 'my.windowsxp.box',
'port' => 4444,
'timeout' => 30000,
)
);
protected function setUp()
{
$this->setBrowserUrl('http://www.example.com/');
}
public function testTitle()
{
$this->open('http://www.example.com/');
$this->assertTitleEquals('Example Web Page');
}
}
?>
PHPUnit_Extensions_SeleniumTestCase を使用すると、
Selenium で実行したテストのカバレッジ情報を収集することができます。
PHPUnit/Extensions/SeleniumTestCase/phpunit_coverage.php をウェブサーバのドキュメントルートディレクトリにコピーします。php.ini ファイルで、PHPUnit/Extensions/SeleniumTestCase/prepend.php と PHPUnit/Extensions/SeleniumTestCase/append.php をそれぞれ auto_prepend_file および auto_append_file に設定します。PHPUnit_Extensions_SeleniumTestCase を継承したテストケースクラスで、protected $coverageScriptUrl = 'http://host/phpunit_coverage.php'; のようにして phpunit_coverage.php スクリプトの URL を指定します。
表 18.2
は、PHPUnit_Extensions_SeleniumTestCase
が提供するさまざまなアサーションメソッドの一覧です。
表18.2 アサーション
| アサーション | 意味 |
|---|---|
void assertAlertPresent() | alert が発生していない場合にエラーを報告します。 |
void assertNoAlertPresent() | alert が発生している場合にエラーを報告します。 |
void assertChecked(string $locator) | $locator で表される要素がチェックされていない場合にエラーを報告します。 |
void assertNotChecked(string $locator) | $locator で表される要素がチェックされている場合にエラーを報告します。 |
void assertConfirmationPresent() | 確認ダイアログが表示されていない場合にエラーを報告します。 |
void assertNoConfirmationPresent() | 確認ダイアログが表示されている場合にエラーを報告します。 |
void assertEditable(string $locator) | $locator で表される要素が編集可能でない場合にエラーを報告します。 |
void assertNotEditable(string $locator) | $locator で表される要素が編集可能な場合にエラーを報告します。 |
void assertElementValueEquals(string $locator, string $text) | $locator で表される要素の値が $text と異なる場合にエラーを報告します。 |
void assertElementValueNotEquals(string $locator, string $text) | $locator で表される要素の値が $text と等しい場合にエラーを報告します。 |
void assertElementContainsText(string $locator, string $text) | $locator で表される要素が $text を含まない場合にエラーを報告します。 |
void assertElementNotContainsText(string $locator, string $text) | $locator で表される要素が $text を含む場合にエラーを報告します。 |
void assertElementPresent(string $locator) | $locator で表される要素が存在しない場合にエラーを報告します。 |
void assertElementNotPresent(string $locator) | $locator で表される要素が存在する場合にエラーを報告します。 |
void assertLocationEquals(string $location) | 現在の位置が $location と異なる場合にエラーを報告します。 |
void assertLocationNotEquals(string $location) | 現在の位置が $location と等しい場合にエラーを報告します。 |
void assertPromptPresent() | プロンプトが表示されていない場合にエラーを報告します。 |
void assertNoPromptPresent() | プロンプトが表示されている場合にエラーを報告します。 |
void assertSelectHasOption(string $selectLocator, string $option) | 指定したオプションが使用できない場合にエラーを報告します。 |
void assertSelectNotHasOption(string $selectLocator, string $option) | 指定したオプションが使用できる場合にエラーを報告します。 |
void assertSelected($selectLocator, $option) | 指定したラベルが選択されていない場合にエラーを報告します。 |
void assertNotSelected($selectLocator, $option) | 指定したラベルが選択されている場合にエラーを報告します。 |
void assertIsSelected(string $selectLocator, string $value) | 指定した値が選択されていない場合にエラーを報告します。 |
void assertIsNotSelected(string $selectLocator, string $value) | 指定した値が選択されている場合にエラーを報告します。 |
void assertSomethingSelected(string $selectLocator) | $selectLocator で表される項目が選択されていない場合にエラーを報告します。 |
void assertNothingSelected(string $selectLocator) | $selectLocator で表される項目が選択されている場合にエラーを報告します。 |
void assertTextPresent(string $pattern) | 指定したパターン $pattern が存在しない場合にエラーを報告します。 |
void assertTextNotPresent(string $pattern) | 指定したパターン $pattern が存在する場合にエラーを報告します。 |
void assertTitleEquals(string $title) | 現在のタイトルが $title と異なる場合にエラーを報告します。 |
void assertTitleNotEquals(string $title) | 現在のタイトルが $title と等しい場合にエラーを報告します。 |
void assertVisible(string $locator) | $locator で表される要素が不可視な場合にエラーを報告します。 |
void assertNotVisible(string $locator) | $locator で表される要素が可視の場合にエラーを報告します。 |
表 18.3 は、
PHPUnit_Extensions_SeleniumTestCase
の 2 つのテンプレートメソッドをまとめたものです。
表18.3 テンプレートメソッド
| メソッド | 意味 |
|---|---|
void defaultAssertions() | テストケース内のすべてのテストで共有するアサーションを上書きします。 このメソッドは、Selenium RC サーバにコマンドが送信されるたびに (送信された後に) コールされます。 |
void sharedAssertions() | テストケース内のすべてのテストで共有するアサーションを上書きします。 このメソッドは、テストが終了する直前にコールされます。 |
使用できるコマンドのリファレンスや実際の使用法については Selenium Core のドキュメント を参照ください。
runSelenese($filename) メソッドを使用すると、
Selenese/HTML の設定から Selenium のテストを実行することができます。
さらに、静的属性 $seleneseDirectory を使用すると、
Selenese/HTML ファイルを含むディレクトリから自動的にテストオブジェクトを作成することができます。
指定したディレクトリ配下を再帰的に走査し、
.htm ファイルを探します。このファイルには
Selenese/HTML が含まれているものとします。例として
例 18.3
を参照ください。
例 18.3: Selenese/HTML ファイルのディレクトリをテストとして使用する
<?php
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
class SeleneseTests extends PHPUnit_Extensions_SeleniumTestCase
{
public static $seleneseDirectory = '/path/to/files';
}
?>
Continuous Integration is a software development practice where members of a team integrate their work frequently, usually each person integrates at least daily, leading to multiple integrations per day. Each integration is verified by an automated build (including test) to detect integration errors as quickly as possible. Many teams find that this approach leads to significantly reduced integration problems and allows a team to develop cohesive software more rapidly. 継続的インテグレーション というソフトウェア開発手法は、チームのメンバーどうしが お互いの開発内容を頻繁に結合させるというものだ、通常は、 最低でも一日に一度、あるいは場合によっては一日に何度もこれを行う。 結合の際には自動ビルド (テストを含む) を行い、 エラーはできるだけ早い段階で検出する。 多くのチームが、この手法によって結合時の問題を激減させている。 また、ソフトウェアをより高速に開発できるようにもなっている。 | ||
| --Martin Fowler | ||
この章では、まず継続的インテグレーションという技法の概要について述べ、 それを PHPUnit にどのように適用するかを説明していきます。
継続的インテグレーションでは、テストを含めたビルド手順を 完全に自動化して何度でも実行できるようにしておく必要があります。 これは、一日に何度も実行されます。 各開発者は、この仕組みによって結合を行い、結合時の問題を減らします。 自動化を実現するには cronjob を使用します。このジョブでは、まずプロジェクトの ソースコードリポジトリ から定期的に最新版をチェックアウトし、 テストを実行し、その結果を利用しやすい形式で出力します。
ここで CruiseControl のような継続的ビルドプロセス用のフレームワークの出番です。 このフレームワークには、メールでの通知や Apache Ant との統合、 またさまざまなバージョン管理ツールとの統合のためのプラグインが用意されています。 またウェブインターフェイスも用意されており、 最新のビルドおよび以前のビルドについての詳細を見ることができます。
次の例では、CruiseControl が
/usr/local/cruisecontrol
にインストールされていることを想定しています。
cd /usr/local/cruisecontrolmkdir -p projects/BankAccount/build/logscd projects/BankAccountsvn co svn://svn.phpunit.de/phpunit/phpunit/branches/release/3.2/PHPUnit/Samples/BankAccount sourcebuild.xml ファイルを編集します。例 19.1: projects/BankAccount/build.xml
<project name="BankAccount" default="build" basedir=".">
<target name="checkout">
<exec dir="${basedir}/source/" executable="svn">
<arg line="up"/>
</exec>
</target>
<target name="test">
<exec dir="${basedir}/source" executable="phpunit" failonerror="true">
<arg line="--log-xml ${basedir}/build/logs/bankaccount.xml BankAccountTest"/>
</exec>
</target>
<target name="build" depends="checkout,test"/>
</project>cd /usr/local/cruisecontrolconfig.xml ファイルを編集します。例 19.2: config.xml
<cruisecontrol>
<project name="BankAccount" buildafterfailed="false">
<plugin
name="svnbootstrapper"
classname="net.sourceforge.cruisecontrol.bootstrappers.SVNBootstrapper"/>
<plugin
name="svn"
classname="net.sourceforge.cruisecontrol.sourcecontrols.SVN"/>
<listeners>
<currentbuildstatuslistener file="logs/${project.name}/status.txt"/>
</listeners>
<bootstrappers>
<svnbootstrapper localWorkingCopy="projects/${project.name}/source/"/>
</bootstrappers>
<modificationset>
<svn localWorkingCopy="projects/${project.name}/source/"/>
</modificationset>
<schedule interval="300">
<ant
anthome="apache-ant-1.7.0"
buildfile="projects/${project.name}/build.xml"/>
</schedule>
<log dir="logs/${project.name}">
<merge dir="projects/${project.name}/build/logs/"/>
</log>
<publishers>
<currentbuildstatuspublisher
file="logs/${project.name}/buildstatus.txt"/>
<email
mailhost="localhost"
buildresultsurl="http://cruise.example.com/buildresults/${project.name}"
skipusers="true"
spamwhilebroken="true"
returnaddress="project@example.com">
<failure address="dev@lists.example.com" reportWhenFixed="true"/>
</email>
</publishers>
</project>
</cruisecontrol>これで CruiseControl サーバを(再)起動できるようになります。
./cruisecontrol.shhttp://localhost:8080/ をブラウザで開きます。
phpUnderControl は
CruiseControl の拡張で、さまざまな PHP 開発ツール、たとえばテスト用の
PHPUnit や
静的コード解析用の
PHP_CodeSniffer、
そして API
ドキュメントの生成用の
PHPDocumentor
を統合します。強力なコマンドラインツールも付属しており、あなたのプロジェクト用に
CruiseControl の XML 設定ファイルを自動生成させることもできます。
次の例では、CruiseControl が
/usr/local/cruisecontrol
にインストールされていることを想定しています。
pear install --alldeps phpunit/phpUnderControlphpuc install /usr/local/cruisecontrolphpuc project --version-control svn
--version-control-url svn://svn.phpunit.de/phpunit/phpunit/branches/release/3.3/PHPUnit/Samples/BankAccount
--test-case BankAccountTest
--test-file BankAccountTest.php
--test-dir .
--project-name BankAccount
/usr/local/cruisecontrol
上のコマンドは、まずプロジェクトのディレクトリとそのプロジェクト用の設定ファイル
build.xml を作成し、ソースリポジトリから初期チェックアウトを行い、
その新しいプロジェクトをグローバル設定ファイル
config.xml に追加します。
これで CruiseControl サーバを(再)起動できるようになります。
cd /usr/local/cruisecontrol./cruisecontrol.shhttp://localhost:8080/ をブラウザで開きます。Apache Maven は、ソフトウェアプロジェクトの管理や理解のためのツールです。 プロジェクト指向モデル (POM) にもとづいた Apache Maven は、 プロジェクトのビルドやレポート、ドキュメントの作成を共通の情報から行います。
PHPUnit の XML ログ機能 (「XML 形式」 を参照ください)
で作成した XML のログファイルは、テストスイート単位で個別の XML に分割しないと
Apache Maven の
surefire plugin で処理することができません。
このプラグインはプロジェクトのテストフェーズで用いるもので、
アプリケーションのユニットテストを実行します。
例 19.4
に、XML の分割を行うための XSLT スタイルシートを示します。
例 19.3
は pom.xml 設定ファイルの例です。
例 19.3: pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- ... -->
<prerequisites>
<maven>2.0.7</maven>
</prerequisites>
<!-- ... -->
<build>
<!-- ... -->
<plugins>
<plugin>
<dependencies>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-trax</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>net.sf.saxon</groupId>
<artifactId>saxon</artifactId>
<version>8.7</version>
</dependency>
</dependencies>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.2-SNAPSHOT</version>
<executions>
<execution>
<id>codecoverage</id>
<phase>pre-site</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<tasks>
<property name="phpunit.codecoverage"
location="${project.reporting.outputDirectory}/phpunit/codecoverage" />
<property name="surefire.reports"
location="${project.build.directory}/surefire-reports" />
<mkdir dir="${phpunit.codecoverage}"/>
<mkdir dir="${surefire.reports}"/>
<!-- ${ant.phpunit} path to PHPUnit executable -->
<!-- ${ant.pear};... this is the include path for your test execution -->
<!-- ${test.AllTests} PHPUnit cmd line args like 'AllTests de/dmc/dashboard/AllTests.php' -->
<exec executable="${ant.phpunit}" dir="${basedir}">
<arg line="-d include_path=${ant.pear};${project.build.sourceDirectory};${project.build.testSourceDirectory}
--report ${phpunit.codecoverage} ${test.AllTests}" />
</exec>
<xslt in="${phpunit.codecoverage}/logfile.xml"
out="${surefire.reports}/xslt.info"
style="src/test/config/phpunit_to_surefire.xslt"
processor="trax">
<!-- this is the output folder for surefire like XML Reports -->
<param name="outputDir" expression="${surefire.reports}"/>
</xslt>
</tasks>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>2.4-SNAPSHOT</version>
<reportSets>
<reportSet>
<reports>
<report>report-only</report>
</reports>
</reportSet>
</reportSets>
</plugin>
</plugins>
</reporting>
</project>例 19.4: phpunit_to_surefire.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="outputDir">.</xsl:param>
<xsl:template match="testsuites">
<xsl:apply-templates select="testsuite"/>
</xsl:template>
<xsl:template match="testsuite">
<xsl:if test="testcase">
<xsl:variable name="outputName" select="./@name"/>
<xsl:result-document href="file:///{$outputDir}/{$outputName}.xml" method="xml">
<xsl:copy-of select="."/>
</xsl:result-document>
</xsl:if>
<xsl:apply-templates select="testsuite"/>
</xsl:template>
</xsl:stylesheet>
PHPUnit の実装はちょっと見慣れないものでしょう。 通常のアプリケーションでは保守しづらくなるようなテクニックを使用したりしています。 PHPUnit がテストを実行する仕組みを知っておくと、 あなたがテストを書く際に役立つこともあるでしょう。
個々のテストは PHPUnit_Framework_Test
のオブジェクトで表され、テストを実行するには PHPUnit_Framework_TestResult
のオブジェクトが必要です。PHPUnit_Framework_TestResult
オブジェクトが PHPUnit_Framework_Test オブジェクトの
run() メソッドに渡され、
このメソッドが実際のテストメソッドを実行します。そこで発生した例外を
PHPUnit_Framework_TestResult オブジェクトに報告します。
これは、Smalltalk の世界では Collecting Parameter
と呼ばれているお決まりのパターンです。複数のメソッドの結果
(ここでは、各テストを起動する run() メソッドの結果)
を一箇所にまとめたい場合は、メソッドにパラメータを追加すればそれが結果を集めてくれるのです。
Erich Gamma と Kent Beck の "JUnit: A Cook's Tour" [GammaBeck1999]
や Kent Beck の "Smalltalk Best Practice Patterns" [Beck1997] [Beck1997-ja]
を参照ください。
PHPUnit がテストを実行するしくみをより深く探るため、 例 20.1 のようなテストクラスを考えてみましょう。
例 20.1: The EmptyTest class
<?php
require_once 'PHPUnit/Framework.php';
class EmptyTest extends PHPUnit_Framework_TestCase
{
private $emptyArray = array();
public function testSize()
{
$this->assertEquals(0, sizeof($this->emptyArray));
}
public function testIsEmpty()
{
$this->assertTrue(empty($this->emptyArray));
}
}
?>
テストが実行されるときに PHPUnit がまず行うのは、テストクラスを
PHPUnit_Framework_Test オブジェクトに変換することです。
ここでは、PHPUnit_Framework_TestSuite には
図 20.1
に見られるように 2 つの EmptyTest インスタンスが含まれます。
PHPUnit_Framework_TestSuite の実行時には、各
EmptyTest が順に実行されます。その中では各自の
setUp() メソッドが実行され、各テストについて
図 20.2
に見られるような新しい $emptyArray を作成します。
こうすることで、あるテストが配列を変更したとしても
それが他のテストに影響を及ぼさないようになります。
仮にグローバル変数やスーパーグローバル変数
($GLOBALS など)
を変更したとしても、それは他のテストには影響を及ぼしません。
グローバル変数やスーパーグローバル変数の保存と復元には
serialize() および
unserialize() を使用しています。
PHP 自体が提供する一部のクラス、たとえば
PDO などのオブジェクトはシリアライズできないので、
このようなオブジェクトが $GLOBALS
配列に格納されている場合は保存に失敗します。
グローバル変数やスーパーグローバル変数の保存と復元を無効にするには、 このようにします。
class MyTest extends PHPUnit_Framework_TestCase
{
protected $backupGlobals = FALSE;
// ...
}
$backupGlobals 属性をたとえば
setUp() メソッドの中で設定したとしても、
なんの効果もないことに注意しましょう。
つまり、テストが実行される際には、ひとつのテストケースクラスが
2 段階のオブジェクトツリーになるということです。各テストは setUp()
で作成された自分自身のコピーの上で実行され、テストは完全に独立して実行されます。
PHPUnit は、リフレクションを使用してインスタンス変数 $name
からメソッド名を取得し、そのテストメソッドを実行します。これは、Smalltalk
の世界では Pluggable Selector と呼ばれているお決まりのパターンです。
Pluggable Selector を使用することでテストをよりシンプルに書くことができますが、
その代わりコードを見ただけではどのメソッドが実行されるのかがわからなくなります。
実行されるメソッドを知るには、実行時のデータの値を調べなければならないのです。
たいていの場合は、PHPUnit の API は単純なものです。単に
PHPUnit_Framework_TestCase を継承したテストケースを作成し、
assertTrue() あるいは assertEquals()
をコールすればよいのです。しかし、PHPUnit をより深く知りたい方のために、
ここではすべてのクラスおよび公開メソッドを説明します。
ほとんどの場合、PHPUnit を使用する際には以下の 5 つのクラスやインターフェイスに出会うことになるでしょう。
PHPUnit_Framework_Assert実際の値が想定した値どおりかどうかを調べるための静的メソッドを集めたもの。
PHPUnit_Framework_Testテストケースとして動作するすべてのオブジェクトのインターフェイス。
PHPUnit_Framework_TestCaseひとつのテスト。
PHPUnit_Framework_TestSuiteテストの集まり。
PHPUnit_Framework_TestResultひとつあるいは複数のテストの実行結果をまとめたもの。
PHPUnit の、5 つの基本クラス/インターフェイスである
PHPUnit_Framework_Assert、
PHPUnit_Framework_Test、
PHPUnit_Framework_TestCase、
PHPUnit_Framework_TestSuite およびand
PHPUnit_Framework_TestResult の関係を
図 21.1
に示します。
PHPUnit 用に書かれたテストケースのほとんどは、間接的に
PHPUnit_Framework_Assert
を継承しています。ここには、
値を自動的にチェックして矛盾を報告するためのメソッドが含まれています。
これらのメソッドは静的に宣言されているので、
あなたが作成したメソッドの中で「規約による設計」方式のアサーションを使用し、
PHPUnit に結果を報告させることができます
(例 21.1 を参照ください)。
例 21.1: 「規約による設計」方式のアサーション
<?php
require_once 'PHPUnit/Framework.php';
class Sample
{
public function aSampleMethod($object)
{
PHPUnit_Framework_Assert::assertNotNull($object);
}
}
$sample = new Sample;
$sample->aSampleMethod(NULL);
?>
Fatal error: Uncaught exception 'PHPUnit_Framework_ExpectationFailedException' with message 'Failed asserting that <null> is not identical to <null>'.
しかし、ほとんどの場合はこれらのアサーションはテストの中で行います。
各アサーションメソッドには 2 種類の方式があります。 エラー時に表示されるメッセージをパラメータとして指定する方法としない方法です。 オプションで指定したメッセージは、通常はテストが失敗したことが報告される場面で 表示されます。これにより、デバッグが楽になります。
例 21.2: メッセージつきのアサーション
<?php
require_once 'PHPUnit/Framework.php';
class MessageTest extends PHPUnit_Framework_TestCase
{
public function testMessage()
{
$this->assertTrue(FALSE, 'これは独自のメッセージです。');
}
}
?>
以下の例は、
例 21.2 のテスト
testMessage()
でメッセージつきのアサーションを使用した場合の出力結果です。
phpunit MessageTest
PHPUnit 3.2.10 by Sebastian Bergmann.
F
Time: 0 seconds
There was 1 failure:
1) testMessage(MessageTest)
これは独自のメッセージです。
Failed asserting that <boolean:false> is true.
/home/sb/MessageTest.php:8
FAILURES!
Tests: 1, Failures: 1.表 21.1 に、すべてのアサーションをまとめます。
表21.1 アサーション
| アサーション | 意味 |
|---|---|
void assertArrayHasKey(mixed $key, array $array) | $array にキー $key が存在しない場合にエラーを報告します。 |
void assertArrayHasKey(mixed $key, array $array, string $message) | $array にキー $key が存在しない場合にエラー $message を報告します。 |
void assertClassHasAttribute(string $attributeName, string $className) | $className::attributeName が存在しない場合にエラーを報告します。 |
void assertClassHasAttribute(string $attributeName, string $className, string $message) | $className::attributeName が存在しない場合にエラー $message を報告します。 |
void assertClassHasStaticAttribute(string $attributeName, string $className) | $className::attributeName が存在しないか、あるいは static でない場合にエラーを報告します。 |
void assertClassHasStaticAttribute(string $attributeName, string $className, string $message) | $className::attributeName が存在しないか、あるいは static でない場合にエラー $message を報告します。 |
void assertContains(mixed $needle, array $haystack) | $needle が $haystack の要素でない場合にエラーを報告します。 |
void assertContains(mixed $needle, array $haystack, string $message) | $needle が $haystack の要素でない場合にエラー $message を報告します。 |
void assertContains(mixed $needle, Iterator $haystack) | $needle が $haystack の要素でない場合にエラーを報告します。 |
void assertContains(mixed $needle, Iterator $haystack, string $message) | $needle が $haystack の要素でない場合にエラー $message を報告します。 |
void assertContains(string $needle, string $haystack) | $needle が $haystack の一部でない場合にエラーを報告します。 |
void assertContains(string $needle, string $haystack, string $message) | $needle が $haystack の一部でない場合にエラー $message を報告します。 |
assertContainsOnly(string $type, array $haystack) | $haystack の中身の型が $type だけではない場合にエラーを報告します。 |
assertContainsOnly(string $type, array $haystack, NULL, string $message) | $haystack の中身の型が $type だけではない場合にエラー $message を報告します。 |
assertContainsOnly(string $type, array $haystack, bool $isNativeType) | $haystack の中身の変数の型が $type だけではない場合にエラーを報告します。$isNativeType はフラグで、$type がネイティブな PHP の型であるかどうかを表します。 |
assertContainsOnly(string $type, array $haystack, bool $isNativeType, string $message) | $haystack の中身の変数の型が $type だけではない場合にエラー $message を報告します。$isNativeType はフラグで、$type がネイティブな PHP の型であるかどうかを表します。 |
assertContainsOnly(string $type, Iterator $haystack) | $haystack の中身の変数の型が $type だけではない場合にエラーを報告します。 |
assertContainsOnly(string $type, Iterator $haystack, NULL, string $message) | $haystack の中身の変数の型が $type だけではない場合にエラー $message を報告します。 |
assertContainsOnly(string $type, Iterator $haystack, bool $isNativeType) | $haystack の中身の変数の型が $type だけではない場合にエラーを報告します。$isNativeType はフラグで、$type がネイティブな PHP の型であるかどうかを表します。 |
assertContainsOnly(string $type, Iterator $haystack, bool $isNativeType, string $message) | $haystack の中身の変数の型が $type だけではない場合にエラー $message を報告します。$isNativeType はフラグで、$type がネイティブな PHP の型であるかどうかを表します。 |
void assertEquals(array $expected, array $actual) | 2 つの配列 $expected と $actual が等しくない場合にエラーを報告します。 |
void assertEquals(array $expected, array $actual, string $message) | 2 つの配列 $expected と $actual が等しくない場合にエラー $message を報告します。 |
void assertEquals(float $expected, float $actual, '', float $delta = 0) | 2 つの float 値 $expected と $actual の誤差が $delta より大きい場合にエラーを報告します。 |
void assertEquals(float $expected, float $actual, string $message, float $delta = 0) | 2 つの float 値 $expected と $actual の誤差が $delta より大きい場合にエラー $message を報告します。 |
void assertEquals(object $expected, object $actual) | 2 つのオブジェクト $expected と $actual が同じ属性値を持たない場合にエラーを報告します。 |
void assertEquals(object $expected, object $actual, string $message) | 2 つのオブジェクト $expected と $actual が同じ属性値を持たない場合にエラー $message を報告します。 |
void assertEquals(string $expected, string $actual) | 2 つの文字列 $expected と $actual が等しくない場合にエラーを報告します。エラーは、2 つの文字列の差分で報告されます。 |
void assertEquals(string $expected, string $actual, string $message) | 2 つの文字列 $expected と $actual が等しくない場合にエラー $message を報告します。エラーは、2 つの文字列の差分で報告されます。 |
void assertEquals(DOMDocument $expected, DOMDocument $actual) | 2 つの DOMDocument オブジェクト $expected と $actual で表される XML ドキュメントが等しくない場合にエラーを報告します。 |
void assertEquals(DOMDocument $expected, DOMDocument $actual, string $message) | 2 つの DOMDocument オブジェクト $expected と $actual で表される XML ドキュメントが等しくない場合にエラー $message を報告します。 |
void assertEquals(mixed $expected, mixed $actual) | 2 つの変数 $expected と $actual が等しくない場合にエラーを報告します。 |
void assertEquals(mixed $expected, mixed $actual, string $message) | 2 つの変数 $expected と $actual が等しくない場合にエラー $message を報告します。 |
void assertFalse(bool $condition) | $condition が TRUE の場合にエラーを報告します。 |
void assertFalse(bool $condition, string $message) | $condition が TRUE の場合にエラー $message を報告します。 |
void assertFileEquals(string $expected, string $actual) | $expected で指定したファイルと $actual で指定したファイルの内容が異なる場合にエラーを報告します。 |
void assertFileEquals(string $expected, string $actual, string $message) | $expected で指定したファイルと $actual で指定したファイルの内容が異なる場合にエラー $message を報告します。 |
void assertFileExists(string $filename) | ファイル $filename が存在しない場合にエラーを報告します。 |
void assertFileExists(string $filename, string $message) | ファイル $filename が存在しない場合にエラー $message を報告します。 |
void assertGreaterThan(mixed $expected, mixed $actual) | $actual の値が $expected の値より大きくない場合にエラーを報告します。 |
void assertGreaterThan(mixed $expected, mixed $actual, string $message) | $actual の値が $expected の値より大きくない場合にエラー $message を報告します。 |
void assertGreaterThanOrEqual(mixed $expected, mixed $actual) | $actual の値が $expected の値以上でない場合にエラーを報告します。 |
void assertGreaterThanOrEqual(mixed $expected, mixed $actual, string $message) | $actual の値が $expected の値以上でない場合にエラー $message を報告します。 |
void assertLessThan(mixed $expected, mixed $actual) | $actual の値が $expected の値より小さくない場合にエラーを報告します。 |
void assertLessThan(mixed $expected, mixed $actual, string $message) | $actual の値が $expected の値より小さくない場合にエラー $message を報告します。 |
void assertLessThanOrEqual(mixed $expected, mixed $actual) | $actual の値が $expected の値以下でない場合にエラーを報告します。 |
void assertLessThanOrEqual(mixed $expected, mixed $actual, string $message) | $actual の値が $expected の値以下でない場合にエラー $message を報告します。 |
void assertNull(mixed $variable) | $variable が NULL でないときにエラーを報告します。 |
void assertNull(mixed $variable, string $message) | $variable が NULL でないときにエラー $message を報告します。 |
void assertObjectHasAttribute(string $attributeName, object $object) | $object->attributeName が存在しない場合にエラーを報告します。 |
void assertObjectHasAttribute(string $attributeName, object $object, string $message) | $object->attributeName が存在しない場合にエラー $message を報告します。 |
void assertRegExp(string $pattern, string $string) | $string が正規表現 $pattern にマッチしない場合にエラーを報告します。 |
void assertRegExp(string $pattern, string $string, string $message) | $string が正規表現 $pattern にマッチしない場合にエラー $message を報告します。 |
void assertSame(object $expected, object $actual) | 2 つの変数 $expected と $actual が同じオブジェクトを参照していない場合にエラーを報告します。 |
void assertSame(object $expected, object $actual, string $message) | 2 つの変数 $expected と $actual が同じオブジェクトを参照していない場合にエラー $message を報告します。 |
void assertSame(mixed $expected, mixed $actual) | 2 つの変数 $expected と $actual が同じ型・同じ値でない場合にエラーを報告します。 |
void assertSame(mixed $expected, mixed $actual, string $message) | 2 つの変数 $expected と $actual が同じ型・同じ値でない場合にエラー $message を報告します。 |
void assertTrue(bool $condition) | $condition が FALSE の場合にエラーを報告します。 |
void assertTrue(bool $condition, string $message) | $condition が FALSE の場合にエラー $message を報告します。 |
void assertType(string $expected, mixed $actual) | 変数 $actual の型が $expected でない場合にエラーを報告します。 |
void assertType(string $expected, mixed $actual, string $message) | 変数 $actual の型が $expected でない場合にエラー $messageを報告します。 |
void assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile) | ファイル $actualFile の XML ドキュメントがファイル $expectedFile の XML ドキュメントと等しくない場合にエラーを報告します。 |
void assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message) | ファイル $actualFile の XML ドキュメントがファイル $expectedFile の XML ドキュメントと等しくない場合にエラー $message を報告します。 |
void assertXmlStringEqualsXmlString(string $expectedXml, string $actualXml) | $actualXml の XML ドキュメントが $expectedXml の XML ドキュメントと等しくない場合にエラーを報告します。 |
void assertXmlStringEqualsXmlString(string $expectedXml, string $actualXml, string $message) | $actualXml の XML ドキュメントが $expectedXml の XML ドキュメントと等しくない場合にエラー $message を報告します。 |
void assertArrayNotHasKey(mixed $key, array $array) | $array にキー $key が存在する場合にエラーを報告します。 |
void assertArrayNotHasKey(mixed $key, array $array, string $message) | $array にキー $key が存在する場合にエラー $message を報告します。 |
void assertClassNotHasAttribute(string $attributeName, string $className) | $className::attributeName が存在する場合にエラーを報告します。 |
void assertClassNotHasAttribute(string $attributeName, string $className, string $message) | $className::attributeName が存在する場合にエラー $message を報告します。 |
void assertClassNotHasStaticAttribute(string $attributeName, string $className) | $className::attributeName が存在し、かつ static である場合にエラーを報告します。 |
void assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message) | $className::attributeName が存在し、かつ static である場合にエラー $message を報告します。 |
void assertNotContains(mixed $needle, array $haystack) | $needle が $haystack の要素である場合にエラーを報告します。 |
void assertNotContains(mixed $needle, array $haystack, string $message) | $needle が $haystack の要素である場合にエラー $message を報告します。 |
void assertNotContains(mixed $needle, Iterator $haystack) | $needle が $haystack の要素である場合にエラーを報告します。 |
void assertNotContains(mixed $needle, Iterator $haystack, string $message) | $needle が $haystack の要素である場合にエラー $message を報告します。 |
void assertNotContains(string $needle, string $haystack) | $needle が $haystack の一部である場合にエラーを報告します。 |
void assertNotContains(string $needle, string $haystack, string $message) | $needle が $haystack の一部である場合にエラー $message を報告します。 |
assertNotContainsOnly(string $type, array $haystack) | $haystack の中身の型が $type だけである場合にエラーを報告します。 |
assertNotContainsOnly(string $type, array $haystack, NULL, string $message) | $haystack の中身の型が $type だけである場合にエラー $message を報告します。 |
assertNotContainsOnly(string $type, array $haystack, bool $isNativeType) | $haystack の中身の変数の型が $type だけである場合にエラーを報告します。$isNativeType はフラグで、$type がネイティブな PHP の型であるかどうかを表します。 |
assertNotContainsOnly(string $type, array $haystack, bool $isNativeType, string $message) | $haystack の中身の変数の型が $type だけである場合にエラー $message を報告します。$isNativeType はフラグで、$type がネイティブな PHP の型であるかどうかを表します。 |
assertNotContainsOnly(string $type, Iterator $haystack) | $haystack の中身の変数の型が $type だけである場合にエラーを報告します。 |
assertNotContainsOnly(string $type, Iterator $haystack, NULL, string $message) | $haystack の中身の変数の型が $type だけである場合にエラー $message を報告します。 |
assertNotContainsOnly(string $type, Iterator $haystack, bool $isNativeType) | $haystack の中身の変数の型が $type だけである場合にエラーを報告します。$isNativeType はフラグで、$type がネイティブな PHP の型であるかどうかを表します。 |
assertNotContainsOnly(string $type, Iterator $haystack, bool $isNativeType, string $message) | $haystack の中身の変数の型が $type だけである場合にエラー $message を報告します。$isNativeType はフラグで、$type がネイティブな PHP の型であるかどうかを表します。 |
void assertNotEquals(array $expected, array $actual) | 2 つの配列 $expected と $actual が等しい場合にエラーを報告します。 |
void assertNotEquals(array $expected, array $actual, string $message) | 2 つの配列 $expected と $actual が等しい場合にエラー $message を報告します。 |
void assertNotEquals(float $expected, float $actual, '', float $delta = 0) | 2 つの float 値 $expected と $actual の誤差が $delta 以下の場合にエラーを報告します。 |
void assertNotEquals(float $expected, float $actual, string $message, float $delta = 0) | 2 つの float 値 $expected と $actual の誤差が $delta 以下の場合にエラー $message を報告します。 |
void assertNotEquals(object $expected, object $actual) | 2 つのオブジェクト $expected と $actual が同じ属性値を持つ場合にエラーを報告します。 |
void assertNotEquals(object $expected, object $actual, string $message) | 2 つのオブジェクト $expected と $actual が同じ属性値を持つ場合にエラー $message を報告します。 |
void assertNotEquals(string $expected, string $actual) | 2 つの文字列 $expected と $actual が等しい場合にエラーを報告します。 |
void assertNotEquals(string $expected, string $actual, string $message) | 2 つの文字列 $expected と $actual が等しい場合にエラー $message を報告します。 |
void assertNotEquals(DOMDocument $expected, DOMDocument $actual) | 2 つの DOMDocument オブジェクト $expected と $actual で表される XML ドキュメントが等しい場合にエラーを報告します。 |
void assertNotEquals(DOMDocument $expected, DOMDocument $actual, string $message) | 2 つの DOMDocument オブジェクト $expected と $actual で表される XML ドキュメントが等しい場合にエラー $message を報告します。 |
void assertNotEquals(mixed $expected, mixed $actual) | 2 つの変数 $expected と $actual が等しい場合にエラーを報告します。 |
void assertNotEquals(mixed $expected, mixed $actual, string $message) | 2 つの変数 $expected と $actual が等しい場合にエラー $message を報告します。 |
void assertFileNotEquals(string $expected, string $actual) | $expected で指定したファイルと $actual で指定したファイルの内容が同じ場合にエラーを報告します。 |
void assertFileNotEquals(string $expected, string $actual, string $message) | $expected で指定したファイルと $actual で指定したファイルの内容が同じ場合にエラー $message を報告します。 |
void assertFileNotExists(string $filename) | ファイル $filename が存在する場合にエラーを報告します。 |
void assertFileNotExists(string $filename, string $message) | ファイル $filename が存在する場合にエラー $message を報告します。 |
void assertNotNull(mixed $variable) | $variable が NULL の場合にエラーを報告します。 |
void assertNotNull(mixed $variable, string $message) | $variable が NULL の場合にエラー $message を報告します。 |
void assertNotRegExp(string $pattern, string $string) | $string が正規表現 $pattern にマッチする場合にエラーを報告します。 |
void assertNotRegExp(string $pattern, string $string, string $message) | $string が正規表現 $pattern にマッチする場合にエラー $message を報告します。 |
void assertNotSame(object $expected, object $actual) | 2 つの変数 $expected と $actual が同じオブジェクトを参照している場合にエラーを報告します。 |
void assertNotSame(object $expected, object $actual, string $message) | 2 つの変数 $expected と $actual が同じオブジェクトを参照している場合にエラー $message を報告します。 |
void assertNotSame(mixed $expected, mixed $actual) | 2 つの変数 $expected と $actual が同じ型・同じ値である場合にエラーを報告します。 |
void assertNotSame(mixed $expected, mixed $actual, string $message) | 2 つの変数 $expected と $actual が同じ型・同じ値である場合にエラー $message を報告します。 |
void assertNotType(string $expected, mixed $actual) | 変数 $actual の型が $expected である場合にエラーを報告します。 |
void assertNotType(string $expected, mixed $actual, string $message) | 変数 $actual の型が $expected である場合にエラー $messageを報告します。 |
void assertObjectNotHasAttribute(string $attributeName, object $object) | $object->attributeName が存在する場合にエラーを報告します。 |
void assertObjectNotHasAttribute(string $attributeName, object $object, string $message) | $object->attributeName が存在する場合にエラー $message を報告します。 |
void assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile) | ファイル $actualFile の XML ドキュメントがファイル $expectedFile の XML ドキュメントと等しい場合にエラーを報告します。 |
void assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message) | ファイル $actualFile の XML ドキュメントがファイル $expectedFile の XML ドキュメントと等しい場合にエラー $message を報告します。 |
void assertXmlStringNotEqualsXmlString(string $expectedXml, string $actualXml) | $actualXml の XML ドキュメントが $expectedXml の XML ドキュメントと等しい場合にエラーを報告します。 |
void assertXmlStringNotEqualsXmlString(string $expectedXml, string $actualXml, string $message) | $actualXml の XML ドキュメントが $expectedXml の XML ドキュメントと等しい場合にエラー $message を報告します。 |
void assertAttributeContains(mixed $needle, string $haystackAttributeName, string $haystackClassName) | $needle が $haystackClassName::$haystackAttributeName の要素でない場合にエラーを報告します。$haystackClassName::$haystackAttributeName は、配列か文字列、あるいは Iterator インターフェイスを実装したオブジェクトです。$haystackClassName::$haystackAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeContains(mixed $needle, string $haystackAttributeName, string $haystackClassName, string $message) | $needle が $haystackClassName::$haystackAttributeName の要素でない場合にエラー $message を報告します。$haystackClassName::$haystackAttributeName は、配列か文字列、あるいは Iterator インターフェイスを実装したオブジェクトです。$haystackClassName::$haystackAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotContains(mixed $needle, string $haystackAttributeName, string $haystackClassName) | $needle が $haystackClassName::$haystackAttributeName の要素である場合にエラーを報告します。$haystackClassName::$haystackAttributeName は、配列か文字列、あるいはIterator インターフェイスを実装したオブジェクトです。$haystackClassName::$haystackAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotContains(mixed $needle, string $haystackAttributeName, string $haystackClassName, string $message) | $needle が $haystackClassName::$haystackAttributeName の要素である場合にエラー $message を報告します。$haystackClassName::$haystackAttributeName は、配列か文字列、あるいはIterator インターフェイスを実装したオブジェクトです。$haystackClassName::$haystackAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeContains(mixed $needle, string $haystackAttributeName, object $haystackObject) | $needle が $haystackObject->haystackAttributeName の要素でない場合にエラーを報告します。$haystackObject->haystackAttributeName は、配列か文字列、あるいは Iterator インターフェイスを実装したオブジェクトです。$haystackObject->haystackAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeContains(mixed $needle, string $haystackAttributeName, object $haystackObject, string $message) | $needle が $haystackObject->haystackAttributeName の要素でない場合にエラー $message を報告します。$haystackObject->haystackAttributeName は、配列か文字列、あるいは Iterator インターフェイスを実装したオブジェクトです。$haystackObject->haystackAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotContains(mixed $needle, string $haystackAttributeName, object $haystackObject) | $needle が $haystackObject->haystackAttributeName の要素である場合にエラーを報告します。$haystackObject->haystackAttributeName は、配列か文字列、あるいは Iterator インターフェースを実装したオブジェクトです。$haystackObject->haystackAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotContains(mixed $needle, string $haystackAttributeName, object $haystackObject, string $message) | $needle が $haystackObject->haystackAttributeName の要素である場合にエラー $message を報告します。$haystackObject->haystackAttributeName は、配列か文字列、あるいは Iterator インターフェースを実装したオブジェクトです。$haystackObject->haystackAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(array $expected, string $actualAttributeName, string $actualClassName) | 2 つの配列 $expected と $actualClassName::$actualAttributeName が等しくない場合にエラーを報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(array $expected, string $actualAttributeName, string $actualClassName, string $message) | 2 つの配列 $expected と $actualClassName::$actualAttributeName が等しくない場合にエラー $message を報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(array $expected, string $actualAttributeName, string $actualClassName) | 2 つの配列 $expected と $actualClassName::$actualAttributeName が等しい場合にエラーを報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(array $expected, string $actualAttributeName, string $actualClassName, string $message) | 2 つの配列 $expected と $actualClassName::$actualAttributeName が等しい場合にエラー $message を報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(float $expected, string $actualAttributeName, string $actualClassName, '', float $delta = 0) | 2 つの float 値 $expected と $actualClassName::$actualAttributeName の誤差が $delta より大きい場合にエラーを報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(float $expected, string $actualAttributeName, string $actualClassName, string $message, float $delta = 0) | 2 つの float 値 $expected と $actualClassName::$actualAttributeName の誤差が $delta より大きい場合にエラー $message を報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(float $expected, string $actualAttributeName, string $actualClassName, '', float $delta = 0) | 2 つの float 値 $expected と $actualClassName::$actualAttributeName の誤差が $delta 以内である場合にエラーを報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(float $expected, string $actualAttributeName, string $actualClassName, string $message, float $delta = 0) | 2 つの float 値 $expected と $actualClassName::$actualAttributeName の誤差が $delta 以内である場合にエラー $message を報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(string $expected, string $actualAttributeName, string $actualClassName) | 2 つの文字列 $expected と $actualClassName::$actualAttributeName が等しくない場合にエラーを報告します。エラーは、2 つの文字列の差分で報告されます。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(string $expected, string $actualAttributeName, string $actualClassName, string $message) | 2 つの文字列 $expected と $actualClassName::$actualAttributeName が等しくない場合にエラー $message を報告します。エラーは、2 つの文字列の差分で報告されます。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(string $expected, string $actualAttributeName, string $actualClassName) | 2 つの文字列 $expected と $actualClassName::$actualAttributeName が等しい場合にエラーを報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(string $expected, string $actualAttributeName, string $actualClassName, string $message) | 2 つの文字列 $expected と $actualClassName::$actualAttributeName が等しい場合にエラー $message を報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(mixed $expected, string $actualAttributeName, string $actualClassName) | 2 つの変数 $expected と $actualClassName::$actualAttributeName が等しくない場合にエラーを報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(mixed $expected, string $actualAttributeName, string $actualClassName, string $message) | 2 つの変数 $expected と $actualClassName::$actualAttributeName が等しくない場合にエラー $message を報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(mixed $expected, string $actualAttributeName, string $actualClassName) | 2 つの変数 $expected と $actualClassName::$actualAttributeName が等しい場合にエラーを報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(mixed $expected, string $actualAttributeName, string $actualClassName, string $message) | 2 つの変数 $expected と $actualClassName::$actualAttributeName が等しい場合にエラー $message を報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(array $expected, string $actualAttributeName, object $actualObject) | 2 つの配列 $expected と $actualObject->actualAttributeName が等しくない場合にエラーを報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(array $expected, string $actualAttributeName, object $actualObject, string $message) | 2 つの配列 $expected と $actualObject->actualAttributeName が等しくない場合にエラーを $message 報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(array $expected, string $actualAttributeName, object $actualObject) | 2 つの配列 $expected と $actualObject->actualAttributeName が等しい場合にエラーを報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(array $expected, string $actualAttributeName, object $actualObject, string $message) | 2 つの配列 $expected と $actualObject->actualAttributeName が等しい場合にエラーを $message 報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(float $expected, string $actualAttributeName, object $actualObject, '', float $delta = 0) | 2 つの float 値 $expected と $actualObject->actualAttributeName の誤差が $delta より大きい場合にエラーを報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(float $expected, string $actualAttributeName, object $actualObject, string $message, float $delta = 0) | 2 つの float 値 $expected と $actualObject->actualAttributeName の誤差が $delta より大きい場合にエラー $message を報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(float $expected, string $actualAttributeName, object $actualObject, '', float $delta = 0) | 2 つの float 値 $expected と $actualObject->actualAttributeName の誤差が $delta 以下の場合にエラーを報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(float $expected, string $actualAttributeName, object $actualObject, string $message, float $delta = 0) | 2 つの float 値 $expected と $actualObject->actualAttributeName の誤差が $delta 以下の場合にエラー $message を報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(string $expected, string $actualAttributeName, object $actualObject) | 2 つの文字列 $expected と $actualObject->actualAttributeName が等しくない場合にエラーを報告します。エラーは、2 つの文字列の差分で報告されます。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(string $expected, string $actualAttributeName, object $actualObject, string $message) | 2 つの文字列 $expected と $actualObject->actualAttributeName が等しくない場合にエラー $message を報告します。エラーは、2 つの文字列の差分で報告されます。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(string $expected, string $actualAttributeName, object $actualObject) | 2 つの文字列 $expected と $actualObject->actualAttributeName が等しい場合にエラーを報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(string $expected, string $actualAttributeName, object $actualObject, string $message) | 2 つの文字列 $expected と $actualObject->actualAttributeName が等しい場合にエラー $message を報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(mixed $expected, string $actualAttributeName, object $actualObject) | 2 つの変数 $expected と $actualObject->actualAttributeName が等しくない場合にエラーを報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeEquals(mixed $expected, string $actualAttributeName, object $actualObject, string $message) | 2 つの変数 $expected と $actualObject->actualAttributeName が等しくない場合にエラー $message を報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(mixed $expected, string $actualAttributeName, object $actualObject) | 2 つの変数 $expected と $actualObject->actualAttributeName が等しい場合にエラーを報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotEquals(mixed $expected, string $actualAttributeName, object $actualObject, string $message) | 2 つの変数 $expected と $actualObject->actualAttributeName が等しい場合にエラー $message を報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeSame(object $expected, string $actualAttributeName, string $actualClassName) | $actualClassName::$actualAttributeName と $actual が同じオブジェクトを参照していない場合にエラーを報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeSame(object $expected, string $actualAttributeName, string $actualClassName, string $message) | $actualClassName::$actualAttributeName と $actual が同じオブジェクトを参照していない場合にエラー $message を報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeSame(mixed $expected, string $actualAttributeName, string $actualClassName) | $actualClassName::$actualAttributeName と $actual が同じ型・同じ値でない場合にエラーを報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeSame(mixed $expected, string $actualAttributeName, string $actualClassName, string $message) | $actualClassName::$actualAttributeName と $actual が同じ型・同じ値でない場合にエラー $message を報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotSame(object $expected, string $actualAttributeName, string $actualClassName) | $actualClassName::$actualAttributeName と $actual が同じオブジェクトを参照している場合にエラーを報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotSame(object $expected, string $actualAttributeName, string $actualClassName, string $message) | $actualClassName::$actualAttributeName と $actual が同じオブジェクトを参照している場合にエラー $message を報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotSame(mixed $expected, string $actualAttributeName, string $actualClassName) | $actualClassName::$actualAttributeName と $actual が同じ型・同じ値である場合にエラーを報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotSame(mixed $expected, string $actualAttributeName, string $actualClassName, string $message) | $actualClassName::$actualAttributeName と $actual が同じ型・同じ値である場合にエラー $message を報告します。$actualClassName::$actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeSame(object $expected, string $actualAttributeName, object $actualObject) | $actualObject->actualAttributeName と $actual が同じオブジェクトを参照していない場合にエラーを報告します。 $actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeSame(object $expected, string $actualAttributeName, object $actualObject, string $message) | $actualObject->actualAttributeName と $actual が同じオブジェクトを参照していない場合にエラー $message を報告します。 $actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeSame(mixed $expected, string $actualAttributeName, object $actualObject) | $actualObject->actualAttributeName と $actual が同じ型、同じ値でない場合にエラーを報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeSame(mixed $expected, string $actualAttributeName, object $actualObject, string $message) | $actualObject->actualAttributeName と $actual が同じ型、同じ値でない場合にエラー $message を報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotSame(object $expected, string $actualAttributeName, object $actualObject) | $actualObject->actualAttributeName と $actual が同じオブジェクトを参照している場合にエラーを報告します。 $actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotSame(object $expected, string $actualAttributeName, object $actualObject, string $message) | $actualObject->actualAttributeName と $actual が同じオブジェクトを参照している場合にエラー $message を報告します。 $actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotSame(mixed $expected, string $actualAttributeName, object $actualObject) | $actualObject->actualAttributeName と $actual が同じ型、同じ値である場合にエラーを報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
void assertAttributeNotSame(mixed $expected, string $actualAttributeName, object $actualObject, string $message) | $actualObject->actualAttributeName と $actual が同じ型、同じ値である場合にエラー $message を報告します。$actualObject->actualAttributeName 属性は public、protected あるいは private のいずれかとなります。 |
もっと複雑なアサーションを行う場合には、
PHPUnit_Framework_Constraint クラスを使用します。
これらは、assertThat() メソッドを使用して
例 21.3
のように評価されます。
例 21.3: assertThat() での制約オブジェクトの使用
<?php
require_once 'PHPUnit/Framework.php';
class ConstraintTest extends PHPUnit_Framework_TestCase
{
public function testNotEquals()
{
$constraint = $this->logicalNot(
$this->equalTo('foo')
);
$this->assertThat('foo', $constraint);
}
}
?>
phpunit ConstraintTest
PHPUnit 3.2.10 by Sebastian Bergmann.
F
Time: 0 seconds
There was 1 failure:
1) testNotEquals(ConstraintTest)
Failed asserting that <string:foo> is not equal to <string:foo>.
/home/sb/ConstraintTest.php:12
FAILURES!
Tests: 1, Failures: 1.
表 21.2 に、
使用できる PHPUnit_Framework_Constraint
の実装をまとめます。
表21.2 制約
| 制約 | 意味 |
|---|---|
PHPUnit_Framework_Constraint_Attribute attribute(PHPUnit_Framework_Constraint $constraint, $attributeName) | 別の制約を、クラスあるいはオブジェクトの属性として適用する制約。 |
PHPUnit_Framework_Constraint_IsAnything anything() | あらゆる入力値を受け入れる制約。 |
PHPUnit_Framework_Constraint_ArrayHasKey arrayHasKey(mixed $key) | 配列が指定したキーを保持していることを保証する制約。 |
PHPUnit_Framework_Constraint_TraversableContains contains(mixed $value) | Iterator インターフェイスを実装している array やオブジェクトが、指定した値を保持していることを保証する制約。 |
PHPUnit_Framework_Constraint_IsEqual equalTo($value, $delta = 0, $maxDepth = 10) | ある値が別の値と等しいかどうかを調べる制約。 |
PHPUnit_Framework_Constraint_Attribute attributeEqualTo($attributeName, $value, $delta = 0, $maxDepth = 10) | ある値がクラスあるいはオブジェクトの属性と等しいかどうかを調べる制約。 |
PHPUnit_Framework_Constraint_FileExists fileExists() | 指定した名前のファイルが存在するかどうかを調べる制約。 |
PHPUnit_Framework_Constraint_GreaterThan greaterThan(mixed $value) | 評価される値が、指定した値より大きいことを保証する制約。 |
PHPUnit_Framework_Constraint_Or greaterThanOrEqual(mixed $value) | 評価される値が、指定した値以上であることを保証する制約。 |
PHPUnit_Framework_Constraint_ClassHasAttribute classHasAttribute(string $attributeName) | 評価されるクラスに、指定した属性があることを保証する制約。 |
PHPUnit_Framework_Constraint_ClassHasStaticAttribute classHasStaticAttribute(string $attributeName) | 評価されるクラスに、指定した static 属性があることを保証する制約。 |
PHPUnit_Framework_Constraint_ObjectHasAttribute hasAttribute(string $attributeName) | 評価されるオブジェクトが、指定した属性を保持していることを保証する制約。 |
PHPUnit_Framework_Constraint_IsIdentical identicalTo(mixed $value) | ある値が別の値と同一であることを保証する制約。 |
PHPUnit_Framework_Constraint_IsInstanceOf isInstanceOf(string $className) | 評価されるオブジェクトが、指定したクラスのインスタンスであることを保証する制約。 |
PHPUnit_Framework_Constraint_IsType isType(string $type) | 評価される値が、指定した型であることを保証する制約。 |
PHPUnit_Framework_Constraint_LessThan lessThan(mixed $value) | 評価される値が、指定した値より小さいことを保証する制約。 |
PHPUnit_Framework_Constraint_Or lessThanOrEqual(mixed $value) | 評価される値が、指定した値以下であることを保証する制約。 |
logicalAnd() | 論理積 (AND)。 |
logicalNot(PHPUnit_Framework_Constraint $constraint) | 論理否定 (NOT)。 |
logicalOr() | 論理和 (OR)。 |
logicalXor() | 排他的論理和 (XOR)。 |
PHPUnit_Framework_Constraint_PCREMatch matchesRegularExpression(string $pattern) | 評価される文字列が、正規表現にマッチすることを保証する制約。 |
PHPUnit_Framework_Constraint_StringContains stringContains(string $string, bool $case) | 評価される文字列が、指定した文字列を含むことを保証する制約。 |
これら以外に、プロジェクトで使用している
オブジェクト固有のアサーションが必要になることもあるでしょう。独自の
Assert クラスを作成し、
そこに独自のアサーションを含めてテストに使用することができます。
アサーションに失敗すると、ボトルネックメソッド
fail(string $message) がコールされ、これは
PHPUnit_Framework_AssertionFailedError をスローします。
このメソッドにもパラメータなしのものがあります。テストでエラーが発生した際に、
fail() を明示的にコールします。
例外が発生することが期待されるテストなどがその例になります。
表 21.3
に、PHPUnit のボトルネックメソッドをまとめます。
markTestIncomplete() および markTestSkipped()
は、テストに対して「未完了」あるいは「省略」の印をつけるために便利なメソッドです。
表21.4 テストに対して「未完了」あるいは「省略」の印をつける
| メソッド | 意味 |
|---|---|
void markTestIncomplete(string $message) | 現在のテストに「未完了」の印をつけます。$message はオプションです。 |
void markTestSkipped(string $message) | 現在のテストに「省略」の印をつけます。$message はオプションです。 |
単体テストとは、もともとクラスの公開インターフェイスをテストするものです。
しかし、時には非公開の属性の内容をテストしたいこともあるでしょう。
readAttribute() メソッドを使用すると、
指定したオブジェクトの属性の値を取得することができます。
表21.5 非公開属性へのアクセス
| メソッド | 意味 |
|---|---|
Mixed readAttribute($object, $attributeName) | オブジェクトの指定した属性 ($attributeName) の値を返します。protected あるいは private である属性についても動作します。 |
PHPUnit_Framework_Test は、
テストとして働くすべてのオブジェクトが使用する、
一般的なインターフェイスです。これを実装したオブジェクトは、
ひとつあるいは複数のテストを表すことになります。
表 21.6
に示す 2 つのメソッドが定義されています。
表21.6 実装することになるメソッド
| メソッド | 意味 |
|---|---|
int count() | テストの数を返します。 |
void run(PHPUnit_Framework_TestResult $result) | テストを実行し、結果を $result で報告します。 |
PHPUnit_Framework_Test の実装クラスとして有名なのは、
PHPUnit_Framework_TestCase および
PHPUnit_Framework_TestSuite の 2 つです。
PHPUnit_Framework_Test を実装したクラスを独自に作成することも可能です。
このインターフェイスはあえて小規模に設計されているので、実装するのは簡単でしょう。
テストケースクラスは PHPUnit_Framework_TestCase
クラスを継承して作成します。たいていの場合は、
テストスイートから自動的にテストを実行させることになるでしょう。
この場合、(規約により) 各テストは test*
という名前のメソッドにしておかなければなりません。
PHPUnit_Framework_TestCase は
PHPUnit_Framework_Test::countTestCases() を実装しており、
これは常に 1 を返します。このクラスで実装されている
PHPUnit_Framework_Test::run(PHPUnit_Framework_TestResult $result)
は、まず setUp() を実行し、テストメソッドを実行し、
それから tearDown() を実行し、その結果を
PHPUnit_Framework_TestResult に報告します。
PHPUnit_Framework_TestCase によって実装されているメソッドを
表 21.7 にまとめます。
表21.7 TestCase
| メソッド | 意味 |
|---|---|
__construct() | テストケースを作成します。 |
__construct(string $name) | 指定した名前のテストケースを作成します。この名前はテストケースを表示する際に使用されます。また、リフレクションで取得するテストメソッドの名前としても使用されます。 |
string getName() | テストケースの名前を返します。 |
void setName($name) | テストケースの名前を設定します。 |
PHPUnit_Framework_TestResult run(PHPUnit_Framework_TestResult $result) | テストケースを実行し、結果を $result に格納するための便利なメソッドです。 |
void runTest() | リフレクションによってテストメソッドを実行されたくない場合に、テストメソッドをオーバーライドします。 |
object getMock($className, [array $methods, [array $arguments, [string $mockClassName, [boolean $callOriginalConstructor, [boolean $callOriginalClone, [boolean $callAutoload]]]]]]) |
指定した $className 用のモックオブジェクト
(第 11 章 を参照ください) を返します。
デフォルトでは、していしたクラスの全メソッドのモックが作成されます。
二番目の (オプションの) パラメータを指定すると、
その配列の要素と一致する名前のメソッドについてのみモックが作成されます。
三番目の (オプションの) パラメータには、
モックオブジェクトのコンストラクタに渡すパラメータを配列で指定します。
四番目の (オプションの) パラメータを使用すると、
モックオブジェクトのクラス名を指定することができます。
五番目の (オプションの) パラメータを使用すると、
元のオブジェクトの __construct() メソッドをコールしないようにすることができます。
六番目の (オプションの) パラメータを使用すると、
元のオブジェクトの __clone() メソッドをコールしないようにすることができます。
七番目の (オプションの) パラメータを使用すると、
モックオブジェクトの作成時に __autoload() を無効にすることができます。
|
void iniSet(string $varName, mixed $newValue) |
このメソッドは ini_set()
関数のラッパーです。テストが終了すると、php.ini
の設定を自動的にもとの値に戻します。
|
void setLocale(integer $category, string $locale, ...) |
このメソッドは setlocale()
関数のラッパーです。テストが終了すると、自動的にもとの設定値に戻します。
|
このクラスには、ふたつのテンプレートメソッド setUp()
および tearDown() が存在します。これをオーバーライドすると、
実行しようとしているテストに関する前処理や後始末を行うことができます。
表 21.8
にこれらのメソッドをまとめます。
テンプレートメソッド assertPreConditions()
および assertPostConditions() を使用すると、
テストケースクラス内のすべてのテストで実行するアサーションを定義することができます。
表21.8 テンプレートメソッド
| メソッド | 意味 |
|---|---|
void setUp() | これをオーバーライドして、fixture の準備、たとえばオブジェクトグラフの作成などを行います。 |
void assertPreConditions() | これをオーバーライドして、テストケースクラス内のすべてのテストで共有するアサーションを実行します。このメソッドがコールされるのは、テストの実行が始まる前に setUp() がコールされた後です。 |
void assertPostConditions() | これをオーバーライドして、テストケースクラス内のすべてのテストで共有するアサーションを実行します。このメソッドがコールされるのは、テストの実行が終わる前に tearDown() がコールされる前です。 |
void tearDown() | これをオーバーライドして、fixture の後始末、たとえばオブジェクトグラフの削除などを行います。 |
PHPUnit_Framework_TestSuite は複数の
PHPUnit_Framework_Test を組み合わせたものです。
簡単に言うと、このクラスには複数のテストケースが含まれており、
テストスイートを実行するとそれらの全てのテストが実行されます。
テストスイートは composite なので、テストスイートの中に別のテストスイートを含め、
さらにそのテストスイートの中には別のテストスイートが含まれており……
といったことも可能です。これにより、
いろいろなところから集めたテストをひとまとめにすることが簡単になります。
run(PHPUnit_Framework_TestResult $result) および
countTestCases() の 2 つに加え、
PHPUnit_Framework_TestSuite は名前つきインスタンス、
名前なしインスタンスを作成するためのメソッドも用意しています。
PHPUnit_Framework_TestSuite
のインスタンスを作成するためのメソッドを
表 21.9
に示します。
表21.9 名前つき、あるいは名前なしインスタンスの作成
| メソッド | 意味 |
|---|---|
__construct() | 空のテストスイートを返します。 |
__construct(string $theClass) | test* という名前のメソッドを持つ、$theClass という名前のクラスのインスタンスを含むテストスイートを返します。$theClass という名前のクラスが存在しない場合は、$theClass という名前の空のテストスイートが返されます。 |
__construct(string $theClass, string $name) | test* という名前のメソッドを持つ $theClass という名前のクラスのインスタンスを含む、$name という名前のテストスイートを返します。 |
__construct(ReflectionClass $theClass) | test* という名前のメソッドを持つ、$theClass が指すクラスのインスタンスを含むテストスイートを返します。 |
__construct(ReflectionClass $theClass, $name) | test* という名前のメソッドを持つ $theClass が指すクラスのインスタンスを含む、$name という名前のテストスイートを返します。 |
string getName() | テストスイートの名前を返します。 |
void setName(string $name) | テストスイートの名前を設定します。 |
void markTestSuiteSkipped(string $message) | 現在処理中のテストスイートを、処理をスキップするように設定します。$message はオプションです。 |
PHPUnit_Framework_TestSuite には、
PHPUnit_Framework_Test
を追加したり取得したりするためのメソッドも用意されています。これを
表 21.10
にまとめます。
表21.10 テストの追加、取得
| メソッド | 意味 |
|---|---|
void addTestSuite(PHPUnit_Framework_TestSuite $suite) | 別のテストスイートを、このテストスイートに追加します。 |
void addTestSuite(string $theClass) | test* という名前のテストメソッドを持つ $theClass という名前のクラスのインスタンスを含むテストスイートを、このテストスイートに追加します。 |
void addTestSuite(ReflectionClass $theClass) | test* という名前のテストメソッドを持つ $theClass で表されるクラスのインスタンスを含むテストスイートを、このテストスイートに追加します。 |
void addTest(PHPUnit_Framework_Test $test) | テストスイートに $test を追加します。 |
void addTestFile(string $filename) | 指定したソースファイルで定義されているクラスをテストスイートに追加します。 |
void addTestFiles(array $filenames) | 指定したソースファイルで定義されているクラスをテストスイートに追加します。 |
int testCount() | このテストスイートに直接登録されているテストの数を返します (再帰的には検索しません)。 |
PHPUnit_Framework_Test[] tests() | このテストスイートに直接登録されているテストを返します。 |
PHPUnit_Framework_Test testAt(int $index) | $index 番目のテストを返します。 |
例 21.4 に、テストスイートを作成して実行する方法を示します。
例 21.4: テストスイートの作成および実行
<?php
require_once 'PHPUnit/Framework.php';
require_once 'ArrayTest.php';
// ArrayTest クラスのテストを含む
// テストスイートを作成します。
$suite = new PHPUnit_Framework_TestSuite('ArrayTest');
// テストを実行します。
$suite->run();
?>
第 7 章 では、
PHPUnit_Framework_TestSuite クラスを使用して
階層化されたテストケースを組み合わせる例を示します。
PHPUnit_Framework_TestSuite クラスには、
ふたつのテンプレートメソッド setUp()
および tearDown() が存在します。
これらはそれぞれ、テストスイートのテストが実行される前と後にコールされます。
表21.11 テンプレートメソッド
| メソッド | 意味 |
|---|---|
void setUp() | テストスイートの最初のテストを実行する前にコールされます。 |
void tearDown() | テストスイートの最後のテストをコールした後でコールされます。 |
これらのテストを実行している間は、実行したテストの数・失敗したテスト・
テストの所要時間などをどこかに保存しておかなければなりません。
これらの結果を収集するのが PHPUnit_Framework_TestResult
です。ひとつの PHPUnit_Framework_TestResult が、
テスト全体で使いまわされます。テストの実行結果や失敗の内容は
PHPUnit_Framework_TestResult に記録されていき、
実行が終了すると、PHPUnit_Framework_TestResult
には全てのテストの概要が含まれるようになります。
PHPUnit_Framework_TestResult は、
テストの進行状況を知りたい他のオブジェクトから参照されることもあります。
例えば、グラフィカルなテストランナーは PHPUnit_Framework_TestResult
を監視し、各テストの開始時にプログレスバーを更新するでしょう。
表 21.12 は、
PHPUnit_Framework_TestResult
の API をまとめたものです。
表21.12 TestResult
| メソッド | 意味 |
|---|---|
void addError(PHPUnit_Framework_Test $test, Exception $e) | 実行中の $test から予期せぬ $e がスローされたことを記録します。 |
void addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e) | 実行中の $test から予期せぬ $e がスローされたことを記録します。 |
PHPUnit_Framework_TestFailure[] errors() | 記録されたエラーを返します。 |
PHPUnit_Framework_TestFailure[] failures() | 記録された失敗を返します。 |
PHPUnit_Framework_TestFailure[] notImplemented() | 記録された未完了テストを返します。 |
int errorCount() | 記録されたエラーの数を返します。 |
int failureCount() | 記録された失敗の数を返します。 |
int notImplementedCount() | 未完了のテストケースの数を返します。 |
int count() | 実行したテストケースの総数を返します。 |
boolean wasSuccessful() | すべてのテストの実行に成功したかどうかを返します。 |
boolean allCompletlyImplemented() | すべてのテストが完全に実装されているかどうかを返します。 |
void collectCodeCoverageInformation(bool $flag) | コードカバレッジ情報の収集を有効あるいは無効にします。 |
array getCodeCoverageInformation() | 収集したコードカバレッジ情報を返します。 |
PHPUnit_Framework_TestResult
のオブザーバを登録したい場合は、PHPUnit_Framework_TestListener
を実装する必要があります。これを登録するには、
表 21.13
に示した addListener() を使用します。
表21.13 TestResult および TestListener
| メソッド | 意味 |
|---|---|
void addListener(PHPUnit_Framework_TestListener $listener) | $listener を登録し、テスト結果の内容が更新された場合にその内容を受け取るようにします。 |
void removeListener(PHPUnit_Framework_TestListener $listener) | 更新を受け取る $listener の登録を解除します。 |
表 21.14 に、テストリスナーが実装するメソッドを示します。 例 22.3 も参照ください。
表21.14 TestListener のコールバック
| メソッド | 意味 |
|---|---|
void addError(PHPUnit_Framework_Test $test, Exception $e) | $test が $e をスローしました。 |
void addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e) | $test がアサーションに失敗し、PHPUnit_Framework_AssertionFailedError 系がスローされました。 |
void addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e) | $test は完了しませんでした。 |
void addSkippedTest(PHPUnit_Framework_Test $test, Exception $e) | $test は実行されませんでした。 |
void startTestSuite(PHPUnit_Framework_TestSuite $suite) | $suite の実行が始まります。 |
void endTestSuite(PHPUnit_Framework_TestSuite $suite) | $suite の実行が終了しました。 |
void startTest(PHPUnit_Framework_Test $test) | $test の実行が始まります。 |
void endTest(PHPUnit_Framework_Test $test) | $test の実行が終了しました。 |
テストを書きやすくする、あるいはテストの実行結果の表示方法を変更するなど、 PHPUnit はさまざまな方法で拡張することができます。 PHPUnit を拡張するための第一歩をここで説明します。
PHPUnit_Framework_TestCase
を継承した抽象サブクラスにユーティリティメソッドを書き、
そのクラスをさらに継承してテストクラスを作成します。
これが、PHPUnit を拡張するための一番簡単な方法です。
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_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_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