【问题标题】:PHPUnit - Cannot redeclare class when processIsolation set to falsePHPUnit - processIsolation 设置为 false 时无法重新声明类
【发布时间】:2015-07-14 23:21:18
【问题描述】:

类似的问题已经在这里问过好几次了

PHPUnit 触发一个新的致命错误

致命错误:无法在第 6 行的 /some/path/to/Validator.php 中重新声明类 Validator

ValidatorTest

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;
    }
}

还有Validator

class Validator
{
    private static $validator = FALSE;

    public function __construct()
    {
    }

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

        return self::$validator;
    }
}

这些类是微不足道的,没有任何自动加载或包含/要求。我从单独的测试目录运行测试,该目录仅包含ValidatorTest.php 和配置文件phpunit.xml(从here 复制)。测试运行良好时

processIsolation="true"

当使用require_once 而不是require 时,这些测试也有效。 所以我的问题是哪个(如果有的话)以及为什么:我是否必须将processIsolation 属性显式设置为true(因为默认为false),使用require_once(不喜欢最好的解决方案)或重构代码(停止使用静态范围、相对路径等)?

【问题讨论】:

  • 你不能在你的环境中为 phpunit 使用自动加载系统而不是使用 require?
  • @Matteo 我没有使用自动加载(至少在 php 中没有)
  • 我建议您尝试编写一个简单的 autoload.php 文件并再次启动测试类。 PHPunit 更喜欢在测试之外自动加载...(需要等)。如果你喜欢我可以试着为你写。告诉我
  • Require_Once() 只会在执行期间加载您的类一次,因此如果它们已经包含,则不会再次包含它们。每次访问时,require 都会加载这些类,这可能会导致您的重复声明。

标签: php unit-testing configuration phpunit


【解决方案1】:

您正在使用dataProvider,因此我们假设test_factory 方法被多次调用。

在这个方法中你有一个require 句子。这将产生您在测试内部或外部遇到的错误。

就像你写的一样:

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

解决方案正在改进您设置测试的方式:

根据定义,测试的前3行应该只执行一次,所以把它们放到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;
    }
}

无论如何,IMO 使用 require_once 并检查常量是否已经定义在测试中是可以的。

【讨论】:

  • 所以如果我没记错的话,test_factory 的执行次数将与dataProvider 中提供的数据一样多?如果是这样,这似乎是一个合法的解决方案 - setUpBeforeClass 是缺失的部分。你知道为什么processIsolation 的解决方案 - 恕我直言,它的意思是“考虑提供的每个新数据,因为这将是一个对过去一无所知的新进程(在这种情况下加载了哪些类)”。
  • 是的。使用 dataProviders 时,对每个数据集执行一次测试。这很方便,因为您可以独立获得有关哪些案例通过和哪些案例不通过的信息。不知道你的问题是什么意思。进程隔离之所以有效,是因为作为不同的进程,之前没有加载其他类,但这是解决小问题的一种非常糟糕的方法。
【解决方案2】:

你必须在你的测试中使用 PHPUnit 注释

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

您可以在特定测试之上使用@runInSeparateProcess 而不是@runTestsInSeparateProcesses。

【讨论】:

  • 你能再解释一下我为什么要这样做吗?
  • 有了这个注释,每个测试都将在一个自己的进程中开始。这可以防止类Validator仍然存在,因为该类只存在于另一个进程中
猜你喜欢
  • 2012-09-12
  • 2012-02-29
  • 1970-01-01
  • 2013-08-07
  • 2011-12-06
  • 2011-03-22
  • 2023-03-29
  • 2016-02-24
  • 2018-02-16
相关资源
最近更新 更多