-->

PHPUnit fails when using Silex SessionServiceProvi

2019-07-02 21:30发布

问题:

I am trying to create a unit test for my Silex application. The unit test class looks something like this:

class PageTest extends WebTestCase {

    public function createApplication() {
        $app = require __DIR__ . '/../../app/app.php';
        $app['debug'] = true;

        $app['session.storage'] = $app->share(function() {
            return new MockArraySessionStorage();
        });

        $app['session.test'] = true;

        unset($app['exception_handler']);
        return $app;
    }

    public function testIndex() {
        $client = $this->createClient();
        $client->request('GET', '/');
        $this->assertTrue($client->getResponse()->isOk());
    }

}

and the silex route it is trying to request look something like this:

$app->get('/', function() use($app) {
    $user     = $app['session']->get('loginUser');

    return $app['twig']->render('views/index.twig', array(
        'user'           => $user,
    ));
});

This causes an RuntimeException: Failed to start the session because headers have already been sent. in \Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage.php:142 with a backtrace that includes the line from the route with $app['session']->get.

It looks like the output that has occured prior to the session start attempt in NativeSessionStorage is actually the PHPUnit output info, since this is the only output I get before the error message:

PHPUnit 3.7.8 by Sebastian Bergmann.

Configuration read from (PATH)\phpunit.xml

E.......

I am a little confused because this error output from phpunit occur in the output before the actual test method is executed. I don't run any other test methods, so it has to be from this error.

How am I supposed to make PHPUnit work on silex routes that use session variables?

回答1:

EDIT after comment below

Ok, I've got the same problem and after an hour browsing the web, I managed to pass tests.

On Silex 2.0-dev, calling $app['session.test'] = true from the WebTestCase class doesn't work at all, it needs to happen in the bootstrap.

Many ways to achieve it, here's two of them :

1/ with phpunit.xml.dist

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
     backupStaticAttributes="false"
     colors="true"
     convertErrorsToExceptions="true"
     convertNoticesToExceptions="true"
     convertWarningsToExceptions="true"
     processIsolation="false"
     stopOnFailure="false"
     syntaxCheck="false"
     bootstrap="./app.php"
>
    <php>
        <env name="TEST" value="true" />          //-> This is the trick
    </php>
    <testsuites>
        <testsuite name="Your app Test Suite">
            <directory>./tests/</directory>
        </testsuite>
    </testsuites>
</phpunit>

then in bootstrap

$app = new \Silex\Application();

...

$app->register(new \Silex\Provider\SessionServiceProvider(), [
    'session.test' => false !== getenv('TEST')
]);

...

return $app;


2/ By extending Silex\Application so you can pass an environment to the constructor

namespace Your\Namespace;

class YourApp extends \Silex\Application
{
    public function __construct($env, array $params = array())
    {
        $this['env'] = $env;

        parent::__construct($params);
    }
}

then in your bootstrap

$env = // Your logic ...

$app = new \Your\Namespace\YourApp($env);

...

$app->register(new \Silex\Provider\SessionServiceProvider(), [
    'session.test' => 'test' === $app['env'],
]);

...

return $app;

Hope that helps, cheers !



回答2:

Ok, I found the answer. It seems to be a bug in Silex.

The problem occured when I register a twig extension BEFORE registering the standard FormServiceProvider. It was not caused by anything inside the twig extension, the error still occure if I strip down the whole extension class to nothing but empty methods.

So, registering of twig extensions in the Silex app object should always be done AFTER registering the providers, at least after the FormServiceProvider (Until the bug is fixed).