Symfony2功能测试和会话持久性

Here is my problem, I add session variable from functional test controller and they don't popup on action targeted by test.

I made a login method and used cookbook advice to log in. I tuned it a bit to allow 2 accounts login : admin and superadmin

/**
     * @param $account string the account to log in with (authorized : superadmin and admin)
     */
    protected function logIn($account)
    {
        $this->session = $this->container->get('session');
        // Cookbook try
        // $this->session = new Session(new MockFileSessionStorage());
        $user = $this->em->getRepository('LCHUserBundle:User')->findOneByUsername($account);
        $firewall = 'admin_area';

        switch($account) {
            case self::SUPER_ADMIN_LOGIN:
                $token = new UsernamePasswordToken($user, $account, $firewall, array('ROLE_SUPER_ADMIN'));
                $this->client->setServerParameter("SERVER_NAME", SiteControllerTest::ROOT_SITE);
                $this->client->setServerParameter("HTTP_HOST", SiteControllerTest::ROOT_SITE);
                break;
            case self::ADMIN_LOGIN:
                $token = new UsernamePasswordToken($user, $account, $firewall, array('ROLE_ADMIN'));

                // Session var I wish to have on my controller action tested
                $this->session->set('currentSite', $this->em->getRepository('LCHMultisiteBundle:Site')->find(1));
                $this->session->save();

                // Use to force server canonical name for admin test
                $this->client->setServerParameter("SERVER_NAME", SiteControllerTest::ROOT_SITE);
                $this->client->setServerParameter("HTTP_HOST", SiteControllerTest::TEST_SITE);
                break;
            default:
                throw new UsernameNotFoundException('Username provided doesn\'t match any authorized account');
        }
        // Save user in session
        $this->session->set('_security_'.$firewall, serialize($token));
        $this->session->set('user', $user);
        $this->session->save();
        // $this->container->set('session', $this->session);

        $cookie = new Cookie($this->session->getName(), $this->session->getId());
        $this->client->getCookieJar()->set($cookie);

My setUp() method does this :

/**
     * {@inheritDoc}
     */
    protected function setUp()
    {
        // Initiates client
        $this->client = static::createClient();

        $this->container = $this->client->getContainer();
        $this->application = new Application($this->client->getKernel());
        $this->application->setAutoExit(false);
        $this->translator = $this->container->get('translator');
        $this->em = $this->container
            ->get('doctrine')
            ->getManager();
    }

You can see that I set session vars for authentication. They appear correctly when I dump session var from tested action, but if I add my currentSite session var, it seems not persisted. As I use the test container provided by client, it should be passed on shouldn't it?

PS : I also overrided Client class, according to this other question.

I found numerous posts on topic but none provide any working solution (this one, this one or that one). I also found this cookbook article.

UPDATE : thanks to Alex Blex remark, I clarify here some parts of my question.

  • setUp() and logIn() are both parts of a custom WebTestCase class, embedding tools needed by my application specificities (such as translator...)
  • My main point here is to set session params in test controller AND retrieve those session params in tested actions

It is not quite clear what you are testing, what your expectations, and where it fails. It would make sense to add the actual test. Why do you need container, translator, etc in your tests at all?

There is nothing special needs to be done for sessions. Consider this example controller, which persists value of 'test' in session and increments it on consecutive calls:

/**
 * @Route("/session")
 * @Method({"GET"})
 */
public function session()
{
    $session = $this->container->get('session');
    $current = $session->get('test', 0);
    $session->set('test', $current + 1);
    return new Response($current);
}

And this test passes:

/**
 * @test
 */
public function session_increments()
{
    $client = static::createClient();        
    $client->request("GET", '/session');
    $this->assertEquals('0', $client->getResponse()->getContent());
    $client->request("GET", '/session');
    $this->assertEquals('1', $client->getResponse()->getContent());
}

Your application logic may be way more complex, but the test should remain pretty much the same - first call to login, second call to assert the response contains something unique to the logged-in user.

UPDATE

Playing with internal implementation in functional tests should be avoided as much as possible, yet, may be essential for some edge-cases, e.g. to simplify testing scenarios.

The following example tests the same controller by setting specific value to the session variable from within the test scenario, and check the value is returned by the app:

/**
 * @test
 */
public function session_returns_value()
{
    $client = static::createClient();        
    $session = $client->getContainer()->get('session');
    $session->set('test', 12);
    $client->request("GET", '/session');
    $this->assertEquals('12', $client->getResponse()->getContent());
}