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

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

Twitter ツイート Hatena Bookmark ブックマーク

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

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

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<?php
// src/AppBundle/Tests/Controller/DefaultControllerTest.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]'] = '[email protected]';
    $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()を実行すれば良いだけです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
$ 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]'] = '[email protected]';
    $client->submit($form);
}

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

まとめ

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

comments powered by Disqus
Built with Hugo
テーマ StackJimmy によって設計されています。