🍰

CakePHP2.x のテスト事情

2022/08/14に公開

対象読者

  • CakePHP2.x でテストコードを書きたいのに公式ドキュメントの手順通りに試してもテストコードを書く環境を作れない方
  • CakePHP2.x でテストコードを書くことになったけど、どう書けばよいのか分からない方

本記事の要約

  • テスト環境を導入する場合は 公式ドキュメント(英語)を見て、手順を進める必要があります
  • Fixture を作成する際は手動で作成せず、テーブル定義からインポートするか bake コマンドで自動生成しましょう
  • 全てのテストを実行する場合は AllTestsTest クラスを作成しましょう

CakePHP2.x にテストを書く環境を構築する方法

公式ドキュメント(日本語)があるので、ドキュメントに沿って手順を進めればテスト環境を導入できます。

が…。

PHP 7 系を使用しているプロジェクトでは PHPUnit5.x 以降を使用する必要があります。

2022/08/09 時点では日本語ドキュメントを見るだけでは PHP 7 系を使用してテストを書く環境を構築することはできません…。

公式ドキュメント(英語)を見て、以下の対応をおこなう必要があります。

To be able to work with PHP 7, it is required to install the version 5, and you'll need to setup the autoloader, and
work around an issue in Composer's autoloader. In your Config/bootstrap.php file add the following:

// Load Composer autoload.
require APP . 'Vendor/autoload.php';

// Remove and re-prepend CakePHP's autoloader as Composer thinks it is the
// most important.
// See: http://goo.gl/kKVJO7
spl_autoload_unregister(array('App', 'load'));
spl_autoload_register(array('App', 'load'), true, true);

上記対応が完了すれば、テスト環境を導入することができると思います。

注意点としては PHPUnit のバージョンが 5 系になってしまうことですが、CakePHP2.x を使用している以上、PHPUnit のバージョンも古いものになるのは仕方ないかと思います。

CakePHP2.x でのテスト実行方法

CLI

基本的には CLI から実行することになるかと思います。

cake コマンドのサブコマンドである test を使用すればテストを実行することができます。

php ./Console/cake.php test app Controller/AppController

ファイル名をパスカルケースで書き、Test サフィックスさえ付与していればテストファイル名は特に制約はないです。

Hoge, HogeTest のようにファイル名を紐付かせる必要はありません。

ハマりポイントとして、Session を使用している場合は --stderr をオプションとして指定しないとエラーでコケます…。

公式ドキュメントにも明記されています。

セッションと相互作用するテストを実行するときは、基本的に --stderr オプションを使うように するとうまくいきます。これにより、 headers_sent warning によってテストが失敗する問題が
解決するでしょう。

ブラウザ

https://book.cakephp.org/2/ja/development/testing.html#id8

やったことがないので、分からないですが、一応できるようです。

個人的には、test.php が間違って本番でも見れちゃうみたいなことが怖いので、やらない方が良さそうな気がしています…。

全てのテストを実行する

コミットするとき、PR を作成するときなどで自分の変更が他のテストの振る舞いに影響していないか確認するために全てのテストを実行したいと思う瞬間があると思います。

全てのテストを実行する用の AllTestsTest クラスを作成することで全てのテストを実行することができます。

class AllTestsTest extends CakeTestSuite {
    public static function suite() {
        $suite = new CakeTestSuite('All tests');
        $suite->addTestDirectoryRecursive(TESTS . 'Case');
        return $suite;
    }
}

AllTestsTest を作成後に cake test app AllTests と実行することで全てのテストが実行可能です。

CakePHP2.x でのテスト記述方法

Fixture の作成方法

CakePHP2.x では Fixture を使用することでテスト用のデータを作成することができます。

Fixture の作成方法としては3種類あります。

  1. 既存のモデルからテーブル情報を読み取り、テスト用 DB にインサートする方法
class ArticleFixture extends CakeTestFixture {
    public $import = 'Article';
}
  1. 参照する DB を指定し、テーブル定義を読み取る
class ArticleFixture extends CakeTestFixture {
    public $import = array('table' => 'articles', 'connection' => 'other');
}
  1. $fields を定義する
class ArticleFixture extends CakeTestFixture {

    public $fields = array(
        'id' => array('type' => 'integer', 'key' => 'primary'),
        'title' => array('type' => 'string', 'length' => 255, 'null' => false),
        'body' => 'text',
        'published' => array('type' => 'integer', 'default' => '0', 'null' => false),
        'created' => 'datetime',
        'updated' => 'datetime'
    );

    public function init() {
        $this->records = array(
            array(
                'id' => 1,
                'title' => 'First Article',
                'body' => 'First Article Body',
                'published' => '1',
                'created' => date('Y-m-d H:i:s'),
                'updated' => date('Y-m-d H:i:s'),
            ),
        );
        parent::init();
    }
}

$fields を作成するのは人間がやることではないので、ツールに任せましょう。

cake コマンドのサブコマンドである bake を使用すれば、モデルから $fields を作成することができます。

Controller のテスト

testAction を使用してテストしていくことになると思います。

public function testAdding() {
    $data = array(
        'Post' => array(
            'title' => 'New post',
            'body' => 'Secret sauce'
        )
    );
    $this->testAction('/posts/add', array('data' => $data, 'method' => 'get'));
    // some assertions.
}

気を付けるポイントとしてはプロダクションコード内で $this->response を書き換えることを忘れないことです。

書き換えをおこなっていないと、テスト実行時に自動生成された MockController の値が response として渡されることになり、ステータスコードが 200 の body も null です。

Model のテスト

App::uses('Article', 'Model');

class ArticleTest extends CakeTestCase {
    public $fixtures = array('app.article');

    public function setUp() {
        parent::setUp();
        $this->Article = ClassRegistry::init('Article');
    }

    public function testPublished() {
        $result = $this->Article->published(array('id', 'title'));
        $expected = array(
            array('Article' => array('id' => 1, 'title' => 'First Article')),
            array('Article' => array('id' => 2, 'title' => 'Second Article')),
            array('Article' => array('id' => 3, 'title' => 'Third Article'))
        );

        $this->assertEquals($expected, $result);
    }
}

App::uses('xxx', 'Model'); の宣言を忘れないように注意です。

所感

2022年にもなって CakePHP2 を使用しているプロダクトは少ないと思いますが、この記事が誰かの役に立てば幸いです。

採用されている言語、フレームワークがどんなものであっても信頼できるテストコードがあれば、いざ言語、フレームワークを置き換えたいとなった場合の道標になるのではないでしょうか。

テストのないコードは悪いコードである。
どれだけうまく書かれているかは関係ない。
どれだけ美しいか、オブジェクト指向か、きちんとカプセル化されているかは関係ない。
テストがあれば、検証しながらコードの動きを素早く変更することができる。
テストがなければ、コードが良くなっているのか悪くなっているのかが本当にはわからない。

レガシーコード改善ガイド ⅵ ページより

GitHubで編集を提案

Discussion