Silex WebTestCase:无法发送已由PHPUnit / Util / Printer发送的会话cookie头

时间:2013-05-01 11:53:13

标签: symfony phpunit functional-testing silex

不完全确定导致问题的原因,特别是因为我已经遵循了官方文档。我也在这里阅读了无数谷歌集团的讨论和问题,但无济于事。

所以我一直在Fabien Potencier的Silex Skeleton之上构建我的应用程序,但我的单元测试在使用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)

目前我只有一个测试类

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配置

<?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>

在controllers.php中,我安装了一个包含表单

的控制器
use Acme\Controller\FacebookAppControllerProvider;

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

看起来像这样

<?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;
    }
}

在app.php中,FormServiceProvider在实例化新的Silex应用程序之后立即注册,并且在添加Twig扩展之前。

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

    return $twig;
}));

2 个答案:

答案 0 :(得分:2)

有一种更好的方法,Symfony允许您启用不使用'session_ *'功能的测试会话模式。我不确定如何在Silex中启用它,但你必须在Symfony中启用模拟文件存储。

storage_id: session.storage.mock_file

Silex的文档在这里:http://silex.sensiolabs.org/doc/providers/session.html

答案 1 :(得分:-2)

两种解决方案:

  1. 在一个独立的过程中运行测试:

    /**
     * @runInSeparateProcess
     */
    public function testAppControllerForm()
    {
        $client = $this->createClient();
        $crawler = $client->request('GET', '/app/form');
    
        $this->assertTrue($client->getResponse()->isOk());
    }
    
  2. 将phpunit的输出重定向到stderr:

    phpunit --stderr <options>