SymfonyのWebTestCaseでServiceContainerが再生成されてモックが使えなくなった

SymfonyのWebTestCaseでServiceContainerが再生成されてモックが使えなくなった

July 15, 2016,
tags: symfony php test


このエントリーをはてなブックマークに追加

最近SymfonyのWebTestCaseが好きになってきた僕です。こんばんわ。
よくある「フォームを表示して値をいれてPOSTする」的なテストでサービスコンテナにモックオブジェクトを登録しても、なぜか差し替わらなくて苦労したので、解決法を共有したいと思います。

まあ、通常であればWebTestCaseでモックを使いたい事はあまりないんですが、外部サービスのAPIを利用してる場合なんかはやっぱりモックが必要になります。
テストのたびにAPI叩くのは流石に・・・。

よくあるフォームを表示して値を入れてPOSTする的なテスト

$ vim  src/AppBundle/Tests/Controller/DefaultControllerTest.php

<?php
...

public function testHoge()
{
    $client = static::createClient();

    $mock = Phake::mock(Hoge::class);
    static::$kernel->getContainer()->set('hoge', $mock);

    $crawler = $client->request('GET', '/admin/hoge/form/');
    $form = $crawler->selectButton('送信する')->form();
    $form['hoge_mail[email]'] = 'hoge@fuga.com';
    $client->submit($form);
}

よくあるこんなテストケース。
フォームを表示してPOSTするWebTestCaseですね。
これ自体は正常に動作するんですよね。

最初の$client->request('GET', '/admin/hoge/form/');でリクエストを送った時は、WebTestCaseのcreateClientメソッドで作ったサービスコンテナが使われているんですよね。 でも$client->submit($form);でリクエストを送った際には違うサービスコンテナが生成されていました。。。

$client->submit($form)でのリクエストの場合のみbootedがfalseになっていた

なぜコンテナが再生成されてしまうのか、ブレークポイントを貼って調べてみたら、どうやら$client->submit($form);のタイミングでHttpKernelのbootedがfalseになっていました。

おそらくbootedがfalseになるということは、2回目のリクエストを送るタイミングでKernel::shutdown()メソッドが実行されているのではないか?と思い調べてみると、やはりshutdown()メソッドが実行されていました。

なぜshutdownが実行されるのか?

$client->request()$client->submit()するとSymfony\Bundle\FrameworkBundle\Client::dodoRequest()メソッドが実行されるのですが、ここに原因がありました。

この部分のコードをみていただければわかるかと思いますが、1回目の$client->request('GET', '/admin/hoge/form/');時は$this->hasPerformedRequestがfalseのためshutdownが実行されません。

しかしその後$client->submit($form);すると$this->hasPerformedRequestがtrueになっているためshutdownが実行されてしまいます。

shutdownを実行されないようにする

これはすごく簡単で、$client->disableReboot()を実行すれば良いだけです。

$ vim  src/AppBundle/Tests/Controller/DefaultControllerTest.php

<?php
...

public function testHoge()
{
    $client = static::createClient();
    $client->disableReboot()

    $mock = Phake::mock(Hoge::class);
    static::$kernel->getContainer()->set('hoge', $mock);

    $crawler = $client->request('GET', '/admin/hoge/form/');
    $form = $crawler->selectButton('送信する')->form();
    $form['hoge_mail[email]'] = 'hoge@fuga.com';
    $client->submit($form);
}

これでカーネルが再生成されないので、幸せになれます。

まとめ

WebTestCaseではサービスコンテナの再生成が行われないように$client->disableReboot()しましょう。

comments powered by Disqus