Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Silex WebTestCase: Cannot send session cookie headers already sent by PHPUnit/Util/Printer

Not entirely sure what's causing the issue, especially since I've followed the official documentation. I've also read countless Google Group discussions and questions here but to no avail.

So I've been building my app on top of Fabien Potencier's Silex Skeleton, but my unit test fails on a page that has a form on it using the FormServiceProvider.

PHPUnit 3.6.12 by Sebastian Bergmann.

Configuration read from /Users/Adam/Sites/AppEngine/phpunit.xml

..E

Time: 0 seconds, Memory: 10.00Mb

There was 1 error:

1) Acme\AppEngineTest::testAppControllerForm
session_start(): Cannot send session cookie - headers already sent by (output started at /usr/local/php5-20120823-092307/lib/php/PHPUnit/Util/Printer.php:173)

I have only one test class at the moment

namespace Acme;

use Silex\WebTestCase;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;

class AppEngineTest extends WebTestCase
{
    public static function setUpBeforeClass()
    {
        parent::setUpBeforeClass();

        if (! defined('BASEPATH')) {
            define('BASEPATH', __DIR__.'/../..');
        }
    }

    public function createApplication()
    {
        $app = require BASEPATH.'/src/app.php';
        $app['debug'] = true;
        $app['exception_handler']->disable();

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

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

        require BASEPATH.'/config/prod.php';
        require BASEPATH.'/src/controllers.php';

        return $app;
    }

    public function testIndex()
    {
        $client = $this->createClient();
        $crawler = $client->request('GET', '/');

        $this->assertTrue($client->getResponse()->isOk());
    }

    public function testAppControllerIndex()
    {
        $client = $this->createClient();
        $crawler = $client->request('GET', '/app/');

        $this->assertEquals($client->getResponse()->isOk());
        $this->assertCount(1, $crawler->filter('p:contains("Please choose an app from the following")'));
    }

    // This test fails
    public function testAppControllerForm()
    {
        $client = $this->createClient();
        $crawler = $client->request('GET', '/app/form');

        $this->assertTrue($client->getResponse()->isOk());
    }
}

PHPUnit configuration

<?xml version="1.0" encoding="UTF-8"?>

<phpunit colors="true"
    bootstrap="vendor/autoload.php"
    backupGlobals="false"
    backupStaticAttributes="false"
    convertErrorsToExceptions="true"
    convertNoticesToExceptions="true"
    convertWarningsToExceptions="true"
    processIsolation="false"
    stopOnFailure="false"
    syntaxCheck="false"
>
    <testsuites>
        <testsuite name="Test Suite">
            <directory>tests/Acme</directory>
        </testsuite>
    </testsuites>

    <filter>
        <whitelist>
            <directory suffix=".php">src/</directory>
        </whitelist>
    </filter>

    <php>
        <server name="HTTP_USER_AGENT" value="PHPUnit"/>
    </php>
</phpunit>

In controllers.php, I have mounted a controller containing the form

use Acme\Controller\FacebookAppControllerProvider;

$app->mount('/app/', new FacebookAppControllerProvider());

Which looks like this

<?php

namespace Acme\Controller;

use Silex\Application;
use Silex\ControllerProviderInterface;
use Silex\Provider\FacebookServiceProvider;
use SlotMachine\Page as PageContainer;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Yaml\Yaml;

class FacebookAppControllerProvider implements ControllerProviderInterface
{
    public function connect(Application $app)
    {
        if (! $app['twig']->hasExtension('silex_asset_path')) {
            throw new \OutOfBoundsException(
                'The `silex_asset_path` Twig extension provided by this library has not been registered.'
            );
        }

        // creates a new controller based on the default route
        $controllers = $app['controllers_factory'];

        $app->register(new FacebookServiceProvider(), [
            'facebook.config' => [
                'appId'          => '1',
                'secret'         => '2',
                'sharedSession'  => true,
                'trustForwarded' => true,
                'fileUpload'     => false
            ],
            'facebook.permissions' => [ 'email' ],
        ]);

        $app['facebook_app.widget_data'] = [
            'foo' => [
                'name' => 'Foo'
            ],
            'bar' => [
                'name' => 'Bar'
            ]
        ];

        $controllers->match('/', function (Application $app) {
            return $app->render("facebook_app/index.html.twig", $app['facebook_app.widget_data']);
        })->method('POST|GET');

        $controllers->match('/app/{widget}/{tp}', function (Request $request, Application $app, $widget, $tp) {
            $slots = new PageContainer(Yaml::parse(BASEPATH.'/src/Acme/Resources/config/'.$widget.'.slots.yml'));

            // some default data for when the form is displayed the first time
            $formData = [
                'forename'  => 'First-Name',
                'surname'   => 'Last-Name',
                'telephone' => 'Telephone',
            ];

            $form = $app->form($formData)
                ->add('forename', 'text', [ 'label' => 'Name' ])
                ->add('surname',  'text', [ 'label' => false  ])
                ->add('telephone')
                ->getForm()
            ; //end

            if ('POST' == $request->getMethod()) {
                $form->bind($request);

                if ($form->isValid()) {
                    $data = $form->getData();

                    // do something with the data

                    // redirect somewhere
                    return $app->redirect('/');
                }
            }

            $pageData = [
                'slot' => $slots->all(),
                'template_number' => $tp,
                'widget' => [
                    'slug' => $widget,
                    'name' => $app['facebook_app.widget_data'][$widget]['name']
                ],
                'form' => $form->createView(),
            ];

            foreach ($pageData['slot'] as $slot => &$card) {
                $card = PageContainer::interpolate($card, [
                    'widget_name' => $pageData['widget']['name']
                ]);
            }

            $app['page_data'] = $pageData;

            return $app->render(sprintf('templates/%s/template_%d.html.twig', $widget, $tp), $app['page_data']);
        })
        ->method('POST|GET')
        ->assert('tp', '\d+')
        ->value('tp', 1)
        ; // end /app/{publication}/{tp} Controller

        return $controllers;
    }
}

In app.php, the FormServiceProvider is registered immediately after instantiating a new Silex Application and also before adding a Twig extension.

$app['twig'] = $app->share($app->extend('twig', function($twig, $app) {
    // add custom globals, filters, tags, ...
    $twig->addExtension(new Acme\Twig\SilexAssetPathExtension($app));

    return $twig;
}));
like image 822
Adam Elsodaney Avatar asked May 08 '26 04:05

Adam Elsodaney


1 Answers

There is a better approach, Symfony allows you to enable a testing session mode that doesnt use the 'session_*' functionality. I am not sure exactly how to enable this in Silex, but you have to enable the mock file storage in Symfony.

storage_id: session.storage.mock_file

Documentation for Silex is here: http://silex.sensiolabs.org/doc/providers/session.html

like image 81
beberlei Avatar answered May 10 '26 18:05

beberlei



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!