外部モジュールとの連携

2010年4月21日(水)
伊丹 久兼

スタブの利用(1)

 さて、一般的にプログラム開発では、ある機能をモジュールに分割して、それぞれ別々に実装していくことが多いと思います。例えば、あるモジュールをテストするために必要な別のモジュールが未実装あるいはバグが多いため、テストが進められないことがあるのではないかと思います。PHPUnitのスタブ機能を使うと、ダミーのクラスを定義してテストを行うことが可能です。

 例えば、書籍の検索システムを作成していると仮定します。テーブル設計の遅れなどの諸事情によって、肝心の検索モジュールが下記のように未実装だとします。

Bookクラス(Book.php)
--------------------------------------------------------------------------------
class Book
{
  /**
   * bookデータベースを検索し、書籍情報の一覧を返す
   */
  public function findByAuthor($author)
  {
    throw new RuntimeException("未実装です...");
  }
}
>
--------------------------------------------------------------------------------

 テスト対象のBookSearchクラスは、コンストラクタの引数にBookオブジェクトを渡すように実装されており、find()メソッドの内部で、Bookクラスで定義されているfindByAuthor()メソッドを呼び出しています。

BookSearchクラス(BookSearch.php)
--------------------------------------------------------------------------------
class BookSearch
{
  /** @var Bookオブジェクト */
  protected $model;

  public function __construct($model)
  {
    $this->model = $model;
  }

  /**
   * 著者名から書籍名の一覧を返す
   */
  public function find($author)
  {
    $arr = array();
    foreach ($this->model->findByAuthor($author) as $book) {
      $arr[] = $book[1];
    }
    return $arr;
  }
}
?>
--------------------------------------------------------------------------------

 当然、このプログラムを実行するとエラーになります。PHPのインタラクティブ・シェルで確かめてみると、やはり、Bookクラスの内部でRunTimeExceptionが発生してエラーとなりました。
--------------------------------------------------------------------------------
$ php -a
Interactive shell

php > include 'Book.php';
php > include 'BookSearch.php';
php > $search = new BookSearch(new Book());
php > $search->find('芥川龍之介');

PHP Warning: Uncaught exception 'RuntimeException' with message '未実装です...' in /tmp/Book.php:9
Stack trace:
#0 /tmp/BookSearch.php(18): Book->findByAuthor('???????????????')
#1 php shell code(1): BookSearch->find('???????????????')
#2 {main}
thrown in /tmp/Book.php on line 9
--------------------------------------------------------------------------------

PHPUnitのスタブ機能を使うと、このような未実装のモジュールを補完してテストを行うことが可能になります。例えば、図3のようなテスト・ケースになります。

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

require_once 'Book.php';
require_once 'BookSearch.php';

class BookSearchTest extends PHPUnit_Framework_TestCase
{
    protected $fixture;

    protected function setUp()
    {
         // テストケースの初期化時にスタブを作成
        $stub = $this->getMock('Book');
         // いつ呼び出されても同じ動作をさせる
        $stub->expects($this->any())
              // スタブが提供するメソッド名と戻り値の定義
             ->method('findByAuthor')
             ->will($this->returnValue(
                        array(
                            array(1, '羅生門'),
                            array(2, '杜子春'))));

        // Bookオブジェクトの代わりにスタブを渡す
        $this->fixture = new BookSearch($stub);
    }

    public function testFind()
    {
        $this->assertContains(
            '羅生門',
            $this->fixture->find('芥川龍之介'),
            '書籍検索結果に羅生門が無い'
        );
    }
}
?>

				
図3: スタブを利用したテスト・ケース(BookSearchStubTest.php)

スタブの利用(2)

 このテスト・ケースでは、setUp()メソッドで、未実装のBookクラスの代わりとなるスタブを作成し、それをBookSearchクラスのコンストラクタに、Bookオブジェクトの代わりに渡しています。こうすることで、テスト・メソッドのtestFind()では、スタブが用いられていることを気にせずにBookSearhオブジェクトを操作することができます。

テスト結果
--------------------------------------------------------------------------------
$ phpunit BookSearchTest.php
PHPUnit 3.4.12 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 5.75Mb

OK (1 test, 2 assertions)
--------------------------------------------------------------------------------

 今回は、データベースのテストや、スタブを利用したテストなど、外部モジュールとの連携が必要な場合に役立つテスト方法について紹介しました。

 次回は、複数のテスト・ケースを一括して実行する方法や、IDEからPHPUnitを実行する方法について紹介する予定です。

PCIアイオス株式会社

テンアートニ(現サイオステクノロジー)、ゼンド・ジャパンなどを経て現在、PCIアイオスに勤務。これまでに組込み開発からWebシステム開発まで幅広く開発を経験。現在は、オープンソースソフトウェアを利用したシステムの開発、運用、パフォーマンスチューニングを担当。特にLAMP(Linux/Apache/MySQL/PHP)環境を得意とする。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています