phunit如何处理有关require / includes的几个文件?

I'm calling phpunit with the argument being a directory (bonus questions: why can't it accept a list of files?) and it's now complaining about a class being declared more than once because of a file included in the previous test!

If i run phpunit firstTest.php; phpunit secondTest.php everything works

But phpunit ./ fails with PHP Fatal error: Cannot redeclare class X

my tests are basically:

include 'class_to_be_tested.php'
class class1Test extends...

and nothing else. And i'm using the option --process-isolation. I could add require_once on my classes, but that's not what i want to be able to test them individually.

shouldn't phpunit follow best testing practices and run one test, clear whatever garbage it have, run another test on a clean state? or am i doing something wrong?

The way phpUnit works means that all your tests are run in the context of a single php program. phpUnit aims to isolate the tests from each other, but they are all run within the same program execution.

PHP's include statement will include the requested file regardless of whether it has been included before. This means that if you include a given class twice, you will get an error the second time. This is happening in your tests because each test is including the same file, but without any consideration to whether it's already been included by one of the other tests.

Solutions:

  • Wrap your include calls with a if(class_exists('classname')) so that you don't include the file if the class has already been defined.

  • Include the files in a phpUnit bootstrap file instead of in the tests.

  • Use include_once (or even require_once) instead of include.

  • Stop including files arbitrarily, and start using an autoloader.

Since you have include rather than include_once and there is no other code shown in your question, the cannot redeclare error could also be that you are including the file again somewhere in the code under test.

Assuming that is not the case, there some behind the scenes things that happen with --process-isolation that can keep the global class declarations. This blog post gives more detail: http://matthewturland.com/2010/08/19/process-isolation-in-phpunit/

Basically, you will want to create your own base TestCase and override the run() method to set the preserveGlobalState to false. This should properly allow all your tests to run together.

The base class would look similar to this (taken from the blog post I referred to):

class MyTestCase extends PHPUnit_Framework_TestCase
{
    public function run(PHPUnit_Framework_TestResult $result = NULL)
    {
       $this->setPreserveGlobalState(false);
       return parent::run($result);
    }
}

Change:

include 'class_to_be_tested.php';
class class1Test extends...

to be:

include_once 'class_to_be_tested.php';
class class1Test extends...

In PHP you need to have a Really Good Reason to use the former.

Regarding why can't it accept a list of files?, I think the design decision is that you generally don't need to. However you can do it by creating a test suite in the phpunit.xml.dist file, see http://www.phpunit.de/manual/current/en/organizing-tests.html#organizing-tests.xml-configuration