第13章 雛形ジェネレータ

PHPUnit Skeleton Generator を使うと、 プロダクションコードのクラスからテストクラスを生成したり その逆を実行したりすることができます。 インストールするには、次のコマンドを実行します。

pear install phpunit/PHPUnit_SkeletonGenerator

テストケースクラスの雛形の作成

既存のコードのテストを記述する際は、 以下のようなコードを何度となく繰り返し記述することになるでしょう。

public function testMethod()
{
}

PHPUnit Skeleton Generator は、既存のクラスのコードを解析して テストケースクラスの雛形を作成することができます。

例 13.1: Calculator クラス

<?php
class Calculator
{
    public function add($a, $b)
    {
        return $a + $b;
    }
}
?>


次の例は、Calculator という名前のクラス (例 13.1 を参照ください) 用のテストクラスの雛形を作成する手順を示すものです。

phpunit-skelgen --test Calculator
PHPUnit Skeleton Generator 1.0.0 by Sebastian Bergmann.

Wrote skeleton for "CalculatorTest" to "/home/sb/CalculatorTest.php".

もとのクラスの各メソッドについて、 出来上がったテストケースクラスのテストケースは不完全な状態 (第 7 章 を参照ください) です。

名前空間つきのクラスと雛形ジェネレータ

名前空間 内で宣言されたクラス用のコードを雛形ジェネレータで生成する際には、 そのクラスの修飾名とクラスが宣言されているソースファイルへのパスを渡さなければなりません。

たとえば、名前空間 project の中でクラス Calculator が宣言されている場合は 次のようにして雛形ジェネレータを実行します。

phpunit-skelgen --test -- "project\Calculator" Calculator.php
PHPUnit Skeleton Generator 1.0.0 by Sebastian Bergmann.

Wrote skeleton for "project\CalculatorTest" to "/home/sb/CalculatorTest.php".

作成されたテストケースクラスを実行した結果を以下に示します。

phpunit --bootstrap Calculator.php --verbose CalculatorTest
PHPUnit 4.0.0 by Sebastian Bergmann.

I

Time: 0 seconds, Memory: 3.50Mb

There was 1 incomplete test:

1) CalculatorTest::testAdd
This test has not been implemented yet.

/home/sb/CalculatorTest.php:38
OK, but incomplete or skipped tests!
Tests: 1, Assertions: 0, Incomplete: 1.

@assert アノテーションを メソッドのコメント部で使用すると、 シンプルではあるけれど意味のあるテストを自動的に生成することができます。 これは不完全なテストケースではありません。 例 13.2 に例を示します。

例 13.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 --bootstrap Calculator.php --verbose CalculatorTest
PHPUnit 4.0.0 by Sebastian Bergmann.

....

Time: 0 seconds, Memory: 3.50Mb

OK (4 tests, 4 assertions)

表 13.1 に、サポートされる @assert の種類と、それがどのようなテストコードに変換されるかをまとめました。

表13.1 サポートされる @assert アノテーション

アノテーション変換後の内容
@assert (...) == XassertEquals(X, method(...))
@assert (...) != XassertNotEquals(X, method(...))
@assert (...) === XassertSame(X, method(...))
@assert (...) !== XassertNotSame(X, method(...))
@assert (...) > XassertGreaterThan(X, method(...))
@assert (...) >= XassertGreaterThanOrEqual(X, method(...))
@assert (...) < XassertLessThan(X, method(...))
@assert (...) <= XassertLessThanOrEqual(X, method(...))
@assert (...) throws X@expectedException X


テストケースクラスからのクラスの雛形の作成

テスト駆動開発ではまずテストを書いてからそのテストの対象となるコードを書くことになりますが、 PHPUnit ではテストケースクラスをもとにしてクラスの雛形を作成することができます。

規約に従って、Unit クラスのテストは UnitTest クラスに記述されることになります。 このテストケースクラスのソースを検索し、 Unit クラスのオブジェクトを参照している変数を見つけて そのオブジェクトがどんなメソッドをコールしているかを調べます。 例として 例 13.4 を見てみましょう。これは、例 13.3 の解析結果をもとにして作成されたものです。

例 13.3: BowlingGameTest クラス

<?php
class BowlingGameTest extends PHPUnit_Framework_TestCase
{
    protected $game;

    protected function setUp()
    {
        $this->game = new BowlingGame;
    }

    protected function rollMany($n, $pins)
    {
        for ($i = 0; $i < $n; $i++) {
            $this->game->roll($pins);
        }
    }

    public function testScoreForGutterGameIs0()
    {
        $this->rollMany(20, 0);
        $this->assertEquals(0, $this->game->score());
    }
}
?>


phpunit-skelgen --class BowlingGameTest
PHPUnit Skeleton Generator 1.0.0 by Sebastian Bergmann.

Wrote skeleton for "BowlingGame" to "./BowlingGame.php".

例 13.4: 作成された BowlingGame クラスの雛形

<?php
/**
 * Generated by PHPUnit_SkeletonGenerator on 2012-01-09 at 16:55:58.
 */
class BowlingGame
{
    /**
     * @todo Implement roll().
     */
    public function roll()
    {
        // Remove the following line when you implement this method.
        throw new RuntimeException('Not yet implemented.');
    }

    /**
     * @todo Implement score().
     */
    public function score()
    {
        // Remove the following line when you implement this method.
        throw new RuntimeException('Not yet implemented.');
    }
}
?>


作成されたクラスに対してテストを実行した結果を以下に示します。

phpunit --bootstrap BowlingGame.php BowlingGameTest
PHPUnit 4.0.0 by Sebastian Bergmann.

E

Time: 0 seconds, Memory: 3.50Mb

There was 1 error:

1) BowlingGameTest::testScoreForGutterGameIs0
RuntimeException: Not yet implemented.

/home/sb/BowlingGame.php:13
/home/sb/BowlingGameTest.php:14
/home/sb/BowlingGameTest.php:20

FAILURES!
Tests: 1, Assertions: 0, Errors: 1.
このページの改善案をGitHubで提案してください!