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!

Adrian
  • 547
  • 1
  • 5
  • 10

2 Answers2

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');
    // ...
}
Ted Kulp
  • 1,434
  • 13
  • 11
  • 1
    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. – Adrian Feb 28 '11 at 14:34
  • This would've been such a pain to work out by myself. +1 for useful answer – Rowan Jul 04 '11 at 20:04
  • 1
    This seems to have been fixed in PHPUnit 3.6: https://github.com/sebastianbergmann/phpunit/pull/352 – scribu Oct 10 '11 at 22:08
  • 13
    This can also be done for all tests via command switch `phpunit --no-globals-backup` (http://www.phpunit.de/manual/3.6/en/textui.html) or via the corresponding attribute in the `phpunit.xml` file `` (http://www.phpunit.de/manual/3.6/en/appendixes.configuration.html#appendixes.configuration.phpunit). – David Weinraub May 10 '12 at 07:24
  • 9
    Also *avoid* the **--process-isolation** option, it results in "Serialization of 'Closure' is not allowed". Tested with PHPUnit 3.7 – Prof. Falken Feb 25 '13 at 15:49
  • 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. – user570605 Mar 07 '14 at 16:11
  • 2
    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. – Sam Jun 18 '14 at 10:06
  • 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) – AbbasAli Hashemian Dec 19 '15 at 20:38
  • 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. – Calamity Jane Jul 29 '16 at 16:19
  • Codeception: You can set `settings.backup_globals: true` in the codeception.yml file. – Daniel Doezema Aug 10 '18 at 16:01
  • Defaults have changed: https://github.com/sebastianbergmann/phpunit/issues/2190 – user2928048 Aug 23 '21 at 19:47
4

You can also try.

<phpunit backupGlobals="false">
    <testsuites>
        <testsuite name="Test">
            <directory>.</directory>
        </testsuite>
    </testsuites>
</phpunit>
hawx
  • 1,629
  • 5
  • 21
  • 37