Symfony 2 + Doctrine 2 + PHPUnit 3.5: Serialization of closure exception
Asked Answered
S

2

38

I tried to find something about this on Google but nothing came out. I have a TestCase class that inherits from WebTestCase, with some methods that I want to use in all my unit/functional tests:

<?php

namespace Application\FaxServerBundle\Test;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;

use Application\FaxServerBundle\DataFixtures\ORM\NetworkConfigurationData;

class TestCase extends WebTestCase
{
    protected $kernel;

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

    public function getEm()
    {
        return $this->getService( 'doctrine.orm.entity_manager' );
    }

    public function getNetworkConfigurationRepository()
    {
        return $this->getEm()->getRepository( 'Application\FaxServerBundle\Entity\NetworkConfiguration' );
    }

    public function loadNetworkConfigurationFixtures()
    {
        $loader = new Loader();
        $loader->addFixture( new NetworkConfigurationData() );

        $this->loadFixtures( $loader );
    }

    public function loadFixtures( $loader )
    {
        $purger     = new ORMPurger();
        $executor   = new ORMExecutor( $this->getEm(), $purger );
        $executor->execute( $loader->getFixtures() );
    }

    protected function getService( $name, $kernel = null )
    {
        return $this->getBootedKernel()->getContainer()->get( $name );
    }

    protected function hasService( $name, $kernel = null )
    {

        return $this->getBootedKernel()->getContainer()->has( $name );
    }

    protected function getBootedKernel()
    {
        $this->kernel = $this->createKernel();

        if ( !$this->kernel->isBooted() ) 
        {
            $this->kernel->boot();
        }

        return $this->kernel;
    }

    public function generateUrl( $client, $route, $parameters = array() )
    {
        return $client->getContainer()->get( 'router' )->generate( $route, $parameters );
    }
}

Then, my unit test:

<?php

namespace Application\FaxServerBundle\Tests\Entity;

use Doctrine\ORM\AbstractQuery;

use Application\FaxServerBundle\Entity;
use Application\FaxServerBundle\Test\TestCase;

class NetworkConfigurationRepositoryTest extends TestCase
{
 public function setUp()
 {
  parent::setUp();

  $this->loadNetworkConfigurationFixtures();
 }

 public function testGetConfiguration()
 {
  $config = $this->getNetworkConfigurationRepository()->getConfigurationArray();

  $this->assertInternalType( 'array', $config );
  $this->assertEquals( 6, count( $config ) );
  $this->assertArrayHasKey( 'id', $config );
  $this->assertArrayHasKey( 'ip', $config );
  $this->assertArrayHasKey( 'gateway', $config );
  $this->assertArrayHasKey( 'subnetMask', $config );
  $this->assertArrayHasKey( 'primaryDns', $config );
  $this->assertArrayHasKey( 'secondaryDns', $config );
 }

 public function testGetConfigurationObject()
 {
  $config = $this->getNetworkConfigurationRepository()->getConfigurationObject();

  $this->assertInternalType( 'object', $config );
 }

 public function testGetConfigurationArray()
 {
  $config = $this->getNetworkConfigurationRepository()->getConfigurationArray();

  $this->assertInternalType( 'array', $config );
 }
}

It was working before, but, suddenly, after I updated my vendors (doctrine included), it began to throw this exception:

3) Application\FaxServerBundle\Tests\Entity\NetworkConfigurationRepositoryTest::testGetConfigurationArray
RuntimeException: PHP Fatal error:  Uncaught exception 'PDOException' with message 'You cannot serialize or unserialize PDO instances' in -:32
Stack trace:
#0 [internal function]: PDO->__sleep()
#1 -(32): serialize(Array)
#2 -(113): __phpunit_run_isolated_test()
#3 {main}

Next exception 'Exception' with message 'Serialization of 'Closure' is not allowed' in -:0
Stack trace:
#0 -(0): serialize()
#1 -(113): __phpunit_run_isolated_test()
#2 {main}
  thrown in - on line 0

I've found that the problem comes from the fixture loading. If I remove the code that loads fixtures, it works.

Does anyone know what could be wrong in my code? Is this the best way of loading fixtures?

Thanks!

Som answered 6/12, 2010 at 12:56 Comment(0)
C
90

Not technically related to your issue. However, I had a really hard time trying to solve the "Serialization of 'Closure' is not allowed" issue while using PHPUnit, and this question is the top Google result.

The problem comes from the fact that PHPUnit serializes all the $GLOBALS in the system to essential back them up while the test is running. It then restores them after the test is done.

However, if you have any closures in your GLOBAL space, it's going to cause problems. There's two ways to solve it.

You can disable the global backup procedure totally by using an annotation.

/**
 * @backupGlobals disabled
 */
class MyTest extends PHPUnit_Framework_TestCase
{
    // ...
}

Or, if you know which variable is causing the problem (look for a lambda in var_dump($GLOBALS)), you can just blacklist the problem variable(s).

class MyTest extends PHPUnit_Framework_TestCase
{
    protected $backupGlobalsBlacklist = array('application');
    // ...
}
Coper answered 27/2, 2011 at 13:51 Comment(11)
Thanks for your answer! this is indeed what I had to do to solve most of my problems with this exception. The only problem I've got apparently came from the Validator component of Symfony 2 in my tests. Sadly, I couldn't figure it out why it happens yet.Som
This would've been such a pain to work out by myself. +1 for useful answerMoriah
This seems to have been fixed in PHPUnit 3.6: github.com/sebastianbergmann/phpunit/pull/352Balch
This can also be done for all tests via command switch phpunit --no-globals-backup (phpunit.de/manual/3.6/en/textui.html) or via the corresponding attribute in the phpunit.xml file <phpunit backupGlobals="false" /> (phpunit.de/manual/3.6/en/…).Ecuador
Also avoid the --process-isolation option, it results in "Serialization of 'Closure' is not allowed". Tested with PHPUnit 3.7Madigan
I use bootstrap file where I initialize some global objects needed in tests and left some references in global scope which caused this exception. Unsetting references from the global scope removed the exception.Stet
I ran into a related problem with --process-isolation - it looks like PHPUnit tries to serialize objects coming in via data providers as well, and you can't stop this even if you use --no-globals-backup. I had to rework my data provider and test to get around this.Skyjack
Thank you @Ted Kulp RE:"This seems to have been fixed in PHPUnit 3.6" I had this problem when I tried PHPUnit 5.1.3 for a simplified test on Yii2 (PHPUnit not Codeception)Lilliamlillian
I have this problem with symfony2 and phpunit 4.8.6 If even version 5.x has the same problem, then I guess an upgrade of phpunit will not help.Platen
Codeception: You can set settings.backup_globals: true in the codeception.yml file.Phylis
Defaults have changed: github.com/sebastianbergmann/phpunit/issues/2190Fenian
D
4

You can also try.

<phpunit backupGlobals="false">
    <testsuites>
        <testsuite name="Test">
            <directory>.</directory>
        </testsuite>
    </testsuites>
</phpunit>
Detachment answered 15/10, 2015 at 15:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.