PHPUnit2 で例外発生をチェックするイディオム

PHPUnit2を使ったテストケースクラスで「例外が正しく発生するか」を検証するにはどのようにコーディングすればよいか?

以下のように「例外が発生することを期待する」コードをtry-catchで囲み、例外が発生しなかった場合に$this->fail()が呼ばれるようにコーディングするのが定石のようです。

<?php
public SomeClassTest extends PHPUnit2_Framework_TestCase {
    public function testFoo()
    {
        $obj = new SomeClass;
 
        try {
            // SomeClass_Exception を発生させるメソッド呼び出し
            $obj->foo();
 
            // 例外が発生しなかった場合は $this->fail() を呼ぶ
            $this->fail('Exception not thrown');
        } catch (SomeClass_Exception $e) {
            // 例外が発生した場合は問題が無いことを明示するために
            // 常に成功するassertメソッドを記述しておく
            $this->assertTrue(true);
        }
    }
}
?>

これで、SomeClass::foo()SomeClass_Exceptionをスローしなかった場合には以下のようにテストの失敗が報告されるようになります。

There was 1 failure:
1) test1(SomeClassTest)
Exception not thrown
/Users/juno/src/SomeClassTest:105

SomeClass_Exceptionがスローされた場合はテストは正常に完了します。

なお、上記のようにテストメソッドをコーディングする際に捕捉する例外クラスとしてExceptionを指定してはいけません。$this->fail()の実体であるPHPUnit2_Framework_Assert::fail()は以下のように定義されており、

public static function fail($message = '') {
    throw new PHPUnit2_Framework_AssertionFailedError($message);
}

ここで例外として投げられているPHPUnit2_Framework_AssertionFailedErrorExceptionを継承しているのです。従って、Exception をキャッチするように記述してしまうと「SomeClass_Exceptionが発生しなかった場合に$this->fail()が投げる例外」も捕捉されてしまい、テストの失敗が報告されなくなってしまいます。

自前のクラスからはExceptionそのものをスローするのではなく、Exceptionのサブクラスを定義するようにしましょう、ということで。

使用可能なタグ <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>