2
votes

A similar question has been asked several times here

PHPUnit triggers a new fatal error

Fatal error: Cannot redeclare class Validator in /some/path/to/Validator.php on line 6

The ValidatorTest class

class ValidatorTest extends PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider data_provider_rules
    */
    public function test_factory($rules)
    {
        define('DIR_SEP', DIRECTORY_SEPARATOR);
        define('SYS_DIR', '.'.DIR_SEP.'..'.DIR_SEP.'classes'); //relative path here

        require SYS_DIR.DIR_SEP.'Validator.php';

        $validator = Validator::factory();

        return $validator;
    }
}

And the Validator class

class Validator
{
    private static $validator = FALSE;

    public function __construct()
    {
    }

    public static function factory()
    {
        if (empty(self::$validator))
        {
            self::$validator = new Validator();
        }

        return self::$validator;
    }
}

The classes are trivial, there is no autoloading or include/require whatsoever. I am running the tests from seperate tests dir that contains only the ValidatorTest.php and configuration file phpunit.xml (copied from here). The tests run fine when

processIsolation="true"

The tests also work when using require_once instead of require. So my question is which (if any) and why: do I have to explicitly the processIsolation attribute to true (since default is false), use require_once (don't feel like the best solution) or refactor the code (stop using static scopes, relative paths, etc.) ?

2
you can't use an autoloading system in you env for phpunit instead of use require? - Matteo
@Matteo I am not using autoloading (at least not in php) - sitilge
i suggest you to try to write a simple autoload.php files and launch the test class again. PHPunit prefer autoload external to the test... (require etc). If you like i can try to write it for you. let me know - Matteo
Require_Once() will only load your classes one time during the execution, so if they are already included, they will not be included again. The require will load the classes each time they are accessed, which is likely causing your duplicate declarations. - Steven Scott

2 Answers

3
votes

You're using a dataProvider, so we'll assume that test_factory method is being invoked more than once.

In this method you have a require sentence. This is going to produce the error you're getting, inside or outside a test.

It's the same as if you write:

require SYS_DIR.DIR_SEP.'Validator.php';
require SYS_DIR.DIR_SEP.'Validator.php';

The solution is enhancing the way you setup your test:

By definition, the first 3 lines of the test should be executed only once, so put them into setUpBeforeClass:

class ValidatorTest extends PHPUnit_Framework_TestCase
{
     public static function setUpBeforeClass()
     {
         define('DIR_SEP', DIRECTORY_SEPARATOR);
         define('SYS_DIR', '.'.DIR_SEP.'..'.DIR_SEP.'classes'); //relative path here
         require SYS_DIR.DIR_SEP.'Validator.php';
     }

    /**
     * @dataProvider data_provider_rules
     */
     public function test_factory($rules)
     {
        $validator = Validator::factory();
        return $validator;
    }
}

Anyway, IMO using require_once and checking if the constants have already been defined is fine in a test.

2
votes

You have to use PHPUnit annotation in top of yor test

 /**
  * @preserveGlobalState
  * @runTestsInSeparateProcesses
  */
 class ValidatorTest extends PHPUnit_Framework_TestCase
{
...
}

Alternative you can use @runInSeparateProcess on top of a specific test instead of @runTestsInSeparateProcesses.