5

I'm running into an issue when trying to run a controller based unit test on a controller method that implements Sessions.

In this case, here is the controller method:

/**
 * @Route("/api/logout")
 */
public function logoutAction()
{
    $session = new Session();
    $session->clear();

    return $this->render('PassportApiBundle:Login:logout.html.twig');
}

And the functional test:

public function testLogout()
{
    $client = static::createClient();
    $crawler = $client->request('GET', '/api/logout');
    $this->assertTrue($client->getResponse()->isSuccessful());
}

The error that is produced:

Failed to start the session because headers have already been sent. (500 Internal Server Error)

I've tried placing in $this->app['session.test'] = true; into the test, but still no go. Has anyone tried resolving an issue like this to unit testing a controller that uses a session?

3
  • 1
    If you look at the test for the Session class (github.com/symfony/symfony/blob/2.1/src/Symfony/Component/…), it looks like you should inject an instance of MockArraySessionStorage to avoid starting the session. Commented Dec 3, 2012 at 23:37
  • I set those lines up, however, how do I pass it to the $client = static::createClient();? Adding those lines alone results to the same error. Commented Dec 4, 2012 at 0:14
  • Yes, the problem is your controller does not offer to inject anything. The new operation has no parameters, and there would be no way to pass anything from the test anyways. But I think Symfony 2 should be able to make it's DI framework usable for this. Have you read for example this: stackoverflow.com/questions/10106195/… Additionally, the Symphony docs suggest getting to the Session like this: $session = $this->getRequest()->getSession(); - this could enable the PHPUnit bootstrapping to use mock session objects Commented Dec 4, 2012 at 0:25

3 Answers 3

13

First of all you should use session object from container. So your action should look more like:

/**
 * @Route("/api/logout")
 */
public function logoutAction()
{
    $session = $this->get('session');
    $session->clear();

    return $this->render('PassportApiBundle:Login:logout.html.twig');
}

And then in your test you can inject service into "client's container". So:

public function testLogout()
{
    $sessionMock = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session')
        ->setMethods(array('clear'))
        ->disableOriginalConstructor()
        ->getMock();

    // example assertion:
    $sessionMock->expects($this->once())
        ->method('clear');

    $client = static::createClient();
    $container = $client->getContainer();
    $container->set('session', $sessionMock);

    $crawler = $client->request('GET', '/api/logout');
    $this->assertTrue($client->getResponse()->isSuccessful());
}

With this code you can do everything you want with your session service. But You have to be aware two things:

  • This mock will be set ONLY for one request (if you want use it in next one, you should set up it again). It's because the client restart kernel and rebuild container between each request.
  • Session handling in Symfony 2.1 is little different than Symfony 2

edit:

I've added an assertion

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. Changing to $session = $this->get('session') made it work perfectly. I don't know why the docs told me to use new Session() though... symfony.com/doc/master/components/http_foundation/sessions.html
8

In my case, it was enough to set

framework:
    session:
        storage_id: session.storage.mock_file

in the config_test.yml. YMMV, and I don't have that much of an idea what I'm actually doing, but it works for me.

1 Comment

I already have that setting in config_test.yml by default, but I got Fatal error: Call to a member function get() on a non-object at the line $session->get('...'); where $session is from $session = $this->container->get('request')->getSession();
2

Here just to complete Cyprian's response.

As Sven explains and when looking at the symfony's doc http://symfony.com/doc/2.3/components/http_foundation/session_testing.html, you have to instanciate the mock session with a MockFileSessionStorage object as first constructor argument.

You need to use :

    use Symfony\Component\HttpFoundation\Session\Storage\MockFileSessionStorage;

And code should be :

    $sessionMock = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')
        ->setMethods(array('clear'))
        ->disableOriginalConstructor()
        ->setConstructorArgs(array(new MockFileSessionStorage()))
        ->getMock();

1 Comment

There might be a better way esp with newer version of Symfony2 but I still need to figure out how - coderwall.com/p/newa2q/testing-with-sessions-symfony2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.