我已经尽职尽责,但我认为到目前为止没有任何问题涉及这个问题。
我有一种情况,我的 PHP 代码根据配置属性生成类定义。
类定义基本上是数据持有者,可以有:
- 公共财产
- 或具有提供 getter/setter 的公共接口的受保护属性。
在某些配置情况下,生成的类名将相同,但它们的文件名将不同。在真实环境中,用户只需根据他们需要的配置生成一组类定义,并在他们的应用程序中使用这些类。那里没有问题,即使用户生成了多组类定义。PHP 会很高兴地继续前进,只要在应用程序中引用一组定义。
我有两个 PHPUnit 测试用例,我使用具有由任一配置生成的不同定义的类进行基本相同的测试。我在所有代码中都使用“require_once”,并在单元测试中使用相同的方法,“require_once”仅包含该测试所需的类文件。
PHPUnit 配置为运行它在我的测试目录下找到的所有测试,并且正常运行。当我为不同的类生成添加测试时,PHPUnit 停止工作并出现 PHP 致命错误:无法重新声明类。
现在,PHPUnit 的默认行为似乎是一次性加载所有测试需要运行的所有类。即使在运行任何测试之前,这也会导致致命的 PHP 错误,因为 PHPUnit 为生成的类选择了第二个定义(它们具有相同的类名但不同的文件名),即使我在每个中只“require_once”了正确的类定义测试文件。
我仍然可以运行测试,但一次只能使用一个测试文件,因此目前我必须手动或通过批处理文件运行所有测试。不可能获得所有测试的完整代码覆盖率报告。
我想要的行为是 PHPUnit 只加载每个测试文件“require_once”ed 的类,而不是它认为是从 phpunit.xml 配置运行的所有测试文件。或者更好的方法是 PHPUnit 只为所有指定的测试加载自己的类,让 PHP 自己为每个测试执行用户“require_once”。
有没有办法解决这个问题?我查看了 PHPUnit 配置设置,但似乎没有一个适合特定问题。我对幕后的 PHPUnit 并不十分熟悉,因此构建另一个测试套件加载器会很困难。
最好做一个简单的例子。这里有几个 PHPUnit 测试文件 foo1Test.php 和 foo2test.php。它们之间的唯一区别是它们需要一个不同的类文件,该类文件具有 Foo 类的定义。所有这些类都是空的并不重要,它们都是完全合法的,并且很好地说明了问题。我只是将所有这些文件放在一个“testfoo”目录中并在它们上运行 PHPunit。你也可以这样做,你会得到相同的结果。
foo1Test.php
<?php
require_once dirname ( __FILE__ ).'/Foo1.php';
class Foo1TestCase extends PHPUnit_Framework_TestCase
{
}
?>
foo2Test.php
<?php
require_once dirname ( __FILE__ ).'/Foo2.php';
class Foo2TestCase extends PHPUnit_Framework_TestCase
{
}
?>
Foo1.php
<?php
class Foo
{
}
?>
Foo2.php
<?php
class Foo
{
}
?>
foo1Test.php 和 foo2Test.php 这两个测试文件是完全有效的文件,并通过以下 PHPunit 命令行运行
phpunit foo1Test.php
phpunit foo2Test.php
如您所料,PHPUnit 未能抱怨没有测试,这很好。
我还有一个标准的 phpunit.xml 配置文件,它将在当前目录下运行所有测试。
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupStaticAttributes="false"
cacheTokens="false"
colors="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
forceCoversAnnotation="false"
mapTestClassNameToCoveredClassName="false"
printerClass="PHPUnit_TextUI_ResultPrinter"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader"
strict="false"
verbose="true">
<testsuite name="fooTests">
<directory>.</directory>
</testsuite>
</phpunit>
我使用此配置运行 PHPUnit,如下所示
phpunit
输出是:
PHP Fatal error: Cannot redeclare class Foo in /var/www/testfoo/Foo2.php on line 4
PHP Stack trace:
PHP 1. {main}() /usr/bin/phpunit:0
PHP 2. PHPUnit_TextUI_Command::main() /usr/bin/phpunit:46
PHP 3. PHPUnit_TextUI_Command->run() /usr/share/php/PHPUnit/TextUI/Command.php:130
PHP 4. PHPUnit_TextUI_Command->handleArguments() /usr/share/php/PHPUnit/TextUI/Command.php:139
PHP 5. PHPUnit_Util_Configuration->getTestSuiteConfiguration() /usr/share/php/PHPUnit/TextUI/Command.php:671
PHP 6. PHPUnit_Util_Configuration->getTestSuite() /usr/share/php/PHPUnit/Util/Configuration.php:768
PHP 7. PHPUnit_Framework_TestSuite->addTestFiles() /usr/share/php/PHPUnit/Util/Configuration.php:848
PHP 8. PHPUnit_Framework_TestSuite->addTestFile() /usr/share/php/PHPUnit/Framework/TestSuite.php:419
PHP 9. PHPUnit_Util_Fileloader::checkAndLoad() /usr/share/php/PHPUnit/Framework/TestSuite.php:358
PHP 10. PHPUnit_Util_Fileloader::load() /usr/share/php/PHPUnit/Util/Fileloader.php:79
PHP 11. include_once() /usr/share/php/PHPUnit/Util/Fileloader.php:95
PHP 12. require_once() /var/www/testfoo/foo2Test.php:3
这就是这个问题的意义所在。尽管测试文件在隔离时完全有效,但 PHPUnit 不会在测试套件下运行测试,因为它已尝试在运行任何测试之前加载整个测试套件所需的所有类。
在我看来,PHPUnit 在这里做错了事情,因为它选择了不应该用于特定测试的依赖项。