Closed4

SymfonyのWebTestCaseでリクエスト前にセッションに任意の値をセットしておく方法

ttskchttskch

最初に思いついた方法

// 一旦、適当なページをリクエスト
$client->request('GET', '/');

// リクエスト先がもしリダイレクトレスポンスを返しても動作するように
while ($client->getResponse() instanceof RedirectResponse) {
    $client->followRedirect();
}

// リクエストからセッションを取得
$session = $client->getRequest()->getSession();

// セッションに任意のデータをセット
$session->set('任意のキー', '任意の値');

$client->request('GET', '/someActionUsesSessionData');
self::assertThat('何かを検査');

これで一応動くけど、絶妙にダサい

ttskchttskch

SessionFactoryをモック

しけちゃんの提案をヒントに、SessionFactory をモックして手動でセットしたセッションデータが保持されるようにしてみた

<?php
// tests/Fake/FakeSessionFactory.php

declare(strict_types=1);

namespace App\Tests\Fake;

use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
use Symfony\Component\DependencyInjection\Attribute\AutowireDecorated;
use Symfony\Component\HttpFoundation\Session\SessionFactoryInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

#[AsDecorator(decorates: 'session.factory')]
final class FakeSessionFactory implements SessionFactoryInterface
{
    public function __construct(
        #[AutowireDecorated]
        private readonly SessionFactoryInterface $decorated,
        private ?SessionInterface $session = null,
    ) {
    }

    public function createSession(): SessionInterface
    {
        return $this->session ??= $this->decorated->createSession();
    }
}
// リブートが有効だとリクエストごとにモックが取り消されてしまうのでこれが必要
$client->disableReboot();

// セッションを手動で開始
$session = $client->getContainer()->get('session.factory')->createSession();

// セッションに任意のデータをセット
$session->set('任意のキー', '任意の値');

$client->request('GET', '/someActionUsesSessionData');
self::assertThat('何かを検査');

スッキリ!

SessionFactory の実装を見れば分かるように、createSession() が呼ばれる度に新しい Session インスタンスが生成されるので、モックせずに本物の session.factorycreateSession() を手動で呼んでセッションデータをセットしても、次にリクエスト実行時に createSession() されるときにはまた新しい Session インスタンスが生成されるのでダメです

ttskchttskch

セッションCookieを自作してクライアントにセット

こちらは SessionFactory をモックする必要なし

// セッションに任意のデータをセットした上で、セッションCookieを作成してクライアントにセット
$session = $client->getContainer()->get('session.factory')->createSession();
$session->set('任意のキー', '任意の値');
$session->save();
$cookie = new Cookie($session->getName(), $session->getId());
$client->getCookieJar()->set($cookie);

$client->request('GET', '/someActionUsesSessionData');
self::assertThat('何かを検査');

SessionFactory をモックする方法と違って $client->disableReboot() が必要なく、副作用が少ないので個人的にはこの方法が一番しっくり来た

参考

このスクラップは5ヶ月前にクローズされました