phpUnitでcakePHPのテスト知見まとめ

投稿者: | 2017-12-15

概要

3日間ぐらいで体得したCakePHPのテストユニットを活用したテストの知見まとめ
チームへの情報共有用を公開しても問題ない文言に差し替えたものです。

Install

参考:CakePHP テスト-3.x

まずPHPUnitをインストールする。

$ php composer.phar require --dev phpunit/phpunit:"^5.7|^6.0"

以下コマンドで実行可能

$ vendor/bin/phpunit

また、テストケースを指定してテストすることも可能。以下例。

$ vendor/bin/phpunit tests/TestCase/Model/Table/ArticlesTableTest

特に、モデルの依存関係が今回面倒なことになっていたり、そもそもまだ書いてないテストが適応されたりするので、
対象のコントローラのみをテストする方が賢い?(もちろん、最後には通しでテストする必要があるだろうが)。

ディレクトリ構造(ざっくり)

project/                   -- プロジェクトフォルダ(名前は色々)
    ├ config/              -- テスト用の設定もここ
    └ tests/               -- テストファイル群
        ├ Fixture/         -- テストに用いるための一時的なデータ群が管理されるフォルダ(後述)
        ├ Controller/      -- コントローラのテストケース
        ├ Model/           -- モデルのテストケース
        └ View/            -- ビューのテストケース(触ってない)

ページを取得できるかどうかのテスト例(get)

以下に示す例はコントローラのテストなのでHogesControllerTest.phpに書く。

public function testLoginGet()
{
    $this->get('/hoges/login');
    $this->assertResponseOk();
}

assertResponseOk()メソッドは、レスポンスに2xx系がくればテストを通す

もっと厳密に、テンプレートが表示されたかどうかをアサートするには

$this->assertTemplate('login');

とすると良い。

他のアサーションメソッドは公式ドキュメントを参照。

何らかのデータをPostするテスト(例:ログインフォーム)

/**
* ログイン成功テスト
*/
public function testLoginPostSuccess()
{
    $this->enableCsrfToken();
    $this->enableSecurityToken();

    $this->post('/hoges/login', [
        'username' => 'user',
        'password' => 'password'
    ]);

    // ログインに成功したら目的のページにリダイレクトする
    $this->assertRedirect(['controller' => 'Hoges', 'action' => 'piyo']);
    $this->assertSession(1, 'Auth.User.id');
}

post()メソッドの引数に乗せて送信する。この例では、loginにユーザ名、passwordにパスワードを乗せ、ログイン成功後のページにリダイレクトされているか、また、セッションにユーザが保存されているか(=>正常にログインできているか)を見ている。

セッションを必要とするメソッドのテスト

例えば、ログイン後にしかアクセスできないページに紐づいているメソッドをテストする場合、

/**
* ログインしている状態でのpiyoアクションのテスト
*/
public function testPiyoGet()
{
    // セッションをセット(ログイン状態にする)
    $this->session([
        'Auth' => [
            'User' => [
                'id' => 1,
                'username' => 'user',
            ]
        ]
    ]);

    //ログイン状態時はそのまま通る
    $this->get('/hoges/piyo');
    $this->assertResponseOk();
}

セッションにAuth以下をセットしてから処理する。逆に、ログインしてない状態では弾くテストをする場合

/**
* ログインしていない状態でのpiyoアクションのテスト
*/
public function testPiyoUnauthenticatedFails()
{
    // ログインしてない状態ではloginにリダイレクトされる
    $this->get('/hoges/piyo');
    $this->assertRedirectContains('/hoges/login');
}

ログインに成功した場合、接続を試みた対象のページから戻る設定があるのでURLは

/hoges/login

ではなく

/hoges/login?redirect=%2Fhoges%2Fpiyo

である(っぽい)。したがって、assertRedirectContains()メソッドを使う方が良い。

DBとFixture

ログイン処理などのテストには、ユーザ名とパスワードをペアを保存したDBが必要である。
テストでは、設定がデフォルトであればテスト用のDBに接続する。
テスト用のDBに接続するための設定はconfig/app.phpに書く。

    'Datasources' => [
        'default' => [
            //省略
        ],

        /**
         * The test connection is used during the test suite.
         */
        'test' => [
            'className' => 'Cake\Database\Connection',
            'driver' => 'Cake\Database\Driver\Mysql',
            'persistent' => false,
            'host' => 'localhost',
            //'port' => 'non_standard_port_number',
            'username' => 'test_cakephp',
            'password' => 'secret',
            'database' => 'test_cake_cms',
            'encoding' => 'utf8',
            'timezone' => 'UTC',
            'cacheMetadata' => true,
            'quoteIdentifiers' => false,
            'log' => false,
            //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
            'url' => env('DATABASE_TEST_URL', null),
        ],
    ],

Fixtureは一時的に使えるデータセットで、tests/Fixtureに入っている。Fixtureはテーブル名に則って、FugasFixture.phpのように名付けられる。bakeでも作れる。大抵はFixtureを生成する元になるテーブル名を指定する。

$ bin/cake bake fixture fugas

Fixtureはロードしないと使えないので必ずロードする。

HogesControllerTestでFugasFixtureを読み込みたい時は、以下のようにする

class HogesControllerTest extends IntegrationTestCase
{
    public $fixtures = ['app.fugas'];
    //省略
}

もちろんModelテスト内でも読み込める。

ログイン機能のテストをするとき、Fixtureのpasswordにはハッシュ後のパスワードを入れる。

//FugasFixture.phpにて

//省略
public $records = [
    [
        'id' => 1,
        //略
        'username' => 'user',
        'password' => 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzz', //ハッシュ後のパスワード
        //略
    ],
];

データをPostし、保存されているかを見るテスト

public function testEditCustomerPost()
{
    $this->enableCsrfToken();
    $this->enableSecurityToken();

    // セッションをセット(ログイン状態にする)
    $this->session([
        'Auth' => [
            'User' => [
                'id' => 1,
                'username' => 'user',
            ]
        ]
    ]);

    //ダミーデータ
    $data = [
        'name' => '鈴木 テス太郎',
        'kana' => 'すずき てすたろう',
        'email' => 'cakePHP@example.com',
        'post' => '999-9999',
        'building' => '鈴木ビル 777',
        'address' => '鈴木県テスト市パス町 9-9-9',
    ];
    //postする
    $this->post('/hoges/edit-customer', $data);

    //正常に保存されているかを見る
    $this->assertResponseSuccess();
    $customers = TableRegistry::get('Customers');
    $query = $customers->find()->where(['email' => $data['email']]);
    $this->assertEquals(1, $query->count());
}

正しい形式でデータをPostしたあと、それがDBに保存されているかをFindで発見する。データがあれば結果は1件以上。whereにはユニークかつ今Postしたデータかどうかを確認できるものが望ましい。

パスワードをPostして変更を見る場合のテスト

use Cake\Auth\DefaultPasswordHasher;

    public function testChangePasswordPost()
    {
       $this->enableCsrfToken();
       $this->enableSecurityToken();

    // セッションをセット(ログイン状態にする)
       $this->session([
           'Auth' => [
               'User' => [
                   'id' => 1,
                   'username' => 'user',
               ]
           ]
       ]);

    //ダミーデータ
       $data = [
           'login_id' => 1,
           'password' => 'password',
           'reinput_password' => 'password'
       ];
       //postする
       $this->post('/hoges/change-password', $data);

    //正常に保存されているかを見る
       $this->assertResponseSuccess();
       $customers = TableRegistry::get('Customers');
       //hasherのcheck()を使ってpostしたデータと保存後のデータを比較
       $hasher = new DefaultPasswordHasher();
       $query = $customers->find()->where(['id' => 1])->first();
       $this->assertEquals(1, $hasher->check($data['password1'], $query->password));
    }

DefaultPasswordHasherのインスタンスからcheck()メソッドを利用して正誤を確かめる。

期待したステータスコードが返ってこない時

200系を期待したけど違うコードが返ってきた!みたいな時に疑うべきこと。

500 -- コントローラ側でエラーが出ている可能性がある。大体テストケースでのfixtureの読み忘れとか。
       コントローラ自体がおかしいならテスト書く前に直せ。

404 -- コントローラ若しくはアクションの指定の仕方が間違っているかも。
       cakaPHPのルーティングを確認する。

400 -- enableCsrfToken()とかenableSecurityToken()の付け忘れが多い。
       コンポーネントが有効かつフォームを扱うなら入れる。

302 -- セッションがセットされておらずloginページに戻された時とかに頻出。

余談

このドキュメントを僕はチームの奴らに聖書と呼ばせています。