CakePHP3でコントローラのテストを書く

このエントリはCakePHP 3 Advent Calendar 2015の14日目のエントリです。

みなさん、CakePHP3、好きですか?
好きですよね。ボクは好きです。

いろいろ好きなところはあるのですが、今日はCakePHP3でコントローラのテストを書くお話です。

CakePHP2の場合

CakePHP2まではコントローラのテストはこんな風に書いていました。

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

コントローラのテストは、コントローラに定義されたアクションをテストする、というイメージですね。

CakePHP3の場合

一方、CakePHP3の場合はこんな風に書きます。

[php]
public function testIndexPostData()
{
$data = [
‘Post’ = [
‘title’ => ‘New post’,
‘body’ => ‘Secret sauce’
]
];
$this->post(‘/posts/add’, $data);

$this->assertResponseOk();
$this->assertResponseNotContains(‘Warning’);

$json = json_decode($this->_response->body());
$this->assertNotNull($json);
}
[/php]

9行目を見て「おっ!」と思ったあなた!正解!
そうなんです。Cookbookでも「Controller Integration Testing」と書かれている通り、HTTPリクエストを作ってコントローラをテストできるんです。

すべてのアクションにとりあえず11行〜12行目を書いておくだけでもかなり安心です。

その他、使えるアサーションメソッドはCookbookのAssertion methodsの項を参照してください。

テストのスケルトンはいつもの様に。

[code]
# bin/cake bake test Controller FooController
[/code]

実行はこんなふうに。

[code]
# vendor/bin/phpunit
[/code]

Cookieやセッションは?

先の例では純粋に/posts/addに対してPOSTを発行するテストでした。
実際のアプリケーションではCookieやセッションベースでの認証が必要なものもあるでしょう。

安心してください。Cookieも使えます。

[php]
$this->cookie($key, $value);
[/php]

簡単。
ただね、1つだけ罠があるんです。
とってもラブリィな罠が。
今日のエントリはこれを書きたかったがためのエントリと言っても過言では無いぐらい。

CakePHP3のCookieComponentって標準で内容が暗号化されます。

cookie-cncrypt

一方、上記の様にテストケースの中に書いたcookie();は(暗号化されずに)そのまま保存されます。

この結果、何が起きるかというと「テストケースの中でそのまま保存されたCookieをコントローラの中では復号して取得しようとして、復号に失敗する。」のです…。

これを防ぐにはどうしたら良いかというと、自分で暗号化してあげればOK。

本当!?というところがあるのですが、自分は下記のように回避しました。

[php]
private function setCookie($key, $value){
$prefix = "Q2FrZQ==.";
$salt = Security::salt();
$this->cookie($key, $prefix.base64_encode(
Security::encrypt($value, $salt)));
}
[/php]

2行目の文字列は、CookieComponentの中でハードコーディングされているプレフィックスです。
このロジックはCookieComponentのソースを読むという原始的な方法でみつけました。

他に何か良い方法あれば、是非教えてください!あまりにカッコ悪い!!

と言う訳で、CakePHP3のコントローラのテストのお話でした。