Using Request and Session in Symfony2 PHPUnit Tests

Request and Session are most frequently used services in Symfony2 application. I very often use them in my repositories by injecting with setContainer().

// src/AcmeBundle/Repository/BaseEntityRepository.php
namespace AcmeBundle\Repository;
use Symfony\Component\DependencyInjection\ContainerInterface;
class BaseEntityRepository extends EntityRepository
{
    protected $container;

    public function setContainer(ContainerInterface $container)
    {
        $this->container = $container;
        return $this;
    }
    // ...
}

// src/AcmeBundle/Repository/DemoRepository.php
namespace AcmeBundle\Repository;
use AmceBundle\Repository\BaseEntityRepository;

class DemoRepository extends BaseEntityRepository
{
    public function foobar()
    {
        $request = $this->container->get('request');
        $session = $this->container->get('session');
        
        // ...
    }
}

The problem is that Request and Session are not available when unit test for the repository method is run in CLI and error is thrown.

$ phpunit -c app/ src/AcmeBundle/Tests/Repository/DemoRepositoryTest
Symfony\Component\DependencyInjection\Exception\InactiveScopeException: 
You cannot create a service ("request") of an inactive scope ("request").

So, Request has to be injected into the container to be available to use in the tests. I added the overriding method initializeContainer() in AppKernel.php that enter scope and injection for Request.

// app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    // ...
    
    protected function initializeContainer()
    {
        parent::initializeContainer();
        if (PHP_SAPI == 'cli') {
            $this->getContainer()->enterScope('request');
            $this->getContainer()->set('request', new \Symfony\Component\HttpFoundation\Request(), 'request');
        }
    }
}

For my TestCase, I have a base class AcmeBundleTestCase that extends Symfony\Bundle\FrameworkBundle\Test\WebTestCase\WebTestCase and that implements setup(). The setup method runs on every test and prepares container and request.

// src/AcmeBundle/Tests/AcmeBundleTestCase.php

namespace AcmeBundle\Tests;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Bundle\FrameworkBundle\Console\Application;

class AcmeBundleTestCase extends WebTestCase
{
    protected $em;
    protected $container;
    protected $request;
    protected static $application;
    protected static $kernel;

    public function setUp()
    {
        parent::setUp();

        self::$kernel = static::createKernel();
        self::$kernel->boot();
        $this->container = self::$kernel->getContainer();
        $this->request = $this->container->get('request');

        self::$application = new Application(self::$kernel);
        
        // ...
    }
}

For Session, I added a new property $session in the base class AcmeBundleTestCase and use the default storage engine with MockArraySessionStorage. You could either use MockFileSessionStorage for your functional tests to persist session data across separate PHP processes.

// src/AcmeBundle/Tests/AcmeBundleTestCase.php

namespace AcmeBundle\Tests;

// ...
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;

class AcmeBundleTestCase extends WebTestCase
{
    // ...
    protected $container;
    protected $request;
    protected $session;
    // ....

    public function setUp()
    {
        parent::setUp();

        self::$kernel = static::createKernel();
        self::$kernel->boot();
        $this->container = self::$kernel->getContainer();
        $this->request = $this->container->get('request');

        // Create Session object
        $this->session = new Session(new MockArraySessionStorage());
        $this->container->set('session',  $this->session);

        self::$application = new Application(self::$kernel);
        
        // ...
    }
}

That’s it! If you have any suggestion on this topic, please leave a comment.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s