PHPUnit测试套件的独立性

11
我在phpunit中运行一系列测试,这些测试存在于单独的测试套件中,其列表由phpunit配置文件控制。当单独运行测试(即不通过配置文件,因此一次只运行一个测试套件)时,测试通过,但是当一起运行时,测试失败。
经过仔细检查,问题在于这些测试套件每个都会加载框架(通过require_once),而该框架根据require_once时的设置进行一些内部配置。似乎在分别运行在phpunit配置文件中单独列出的测试套件之间,各种事物持续存在。在这种特殊情况下,该框架已被视为已加载。
那么,是否有一种方法可以让phpunit独立地执行一系列测试套件,即相当于一次只运行一个测试套件? (phpunit由CruiseControl在自动测试机上触发,在提交之前由开发人员在本地触发。)我尝试了诸如'--process-isolation'和'--no-globals-backup'等选项,但没有成功。
快速示例可说明问题的是一个“constant.php”文件:
<?php
if (defined('XYZZY')) define('TEST', 1);
else define('TEST', 2);

一个测试套件'TestOne.php':

<?php
define('XYZZY', "");
require_once('constant.php');
class TestOne extends PHPUnit_Framework_TestCase
{
   public function testOne()
   {
      $this->assertEquals(TEST, 1);
   }
}
一个类似的测试套件'TestTwo.php':
<?php
require_once('constant.php');
class TestTwo extends PHPUnit_Framework_TestCase
{
   public function testTwo()
   {
      $this->assertEquals(TEST, 2);
   }
}

还有一个PHPUnit配置文件:

<phpunit>
  <testsuites>
    <testsuite name="First">
      <file>./TestOne.php</file>
    </testsuite>

    <testsuite name="Second">
      <file>./TestTwo.php</file>
    </testsuite>
  </testsuites>
</phpunit>
2个回答

1

我正在尝试为您提供一些关于--process-isolation和no backup global的工作示例。

希望您能够使用它们使您的测试工作正常。如果不能,请给我留言,我会尽快回复您。

我已经在3.4.15上测试了大部分内容,并且所有内容都适用于HEAD(3.5.x也应该可以)

最简单的方法是:

<?php
class Test extends PHPUnit_Framework_TestCase
{
    public function testOne()
    {
        define('FOO', 'BAR');
        $this->assertEquals('BAR', FOO);
    }

    public function testTwo()
    {
        define('FOO', 'BAZ');
        $this->assertEquals('BAZ', FOO);
    }
}

并生成:

phpunit --process-isolation processTest.php

PHPUnit @package_version@ 由Sebastian Bergmann开发。

..

时间:0秒,内存:1.25Mb

OK(2个测试,2个断言)

因此,如果您将测试分组到测试套件中,或者可以为每个测试用例运行设置(取决于其成本如何),则可以使用以下简单方法:

<?php
class Test extends PHPUnit_Framework_TestCase
{
    public function setUp() 
    {
        // your bootstrap for testsuite X
        // Maybe put this in a baseclass for that suite ?
    }

    public function testOne()
    {
        $this->assertEquals('BAR', FOO);
    }

简而言之

确保在进入测试(和进程隔离)后运行设置代码。

如果其他方法都失败了,我们无法解决:

使用runkit扩展(不安全,仅作为最后的手段)可以调用函数,例如runkit-constant-remove()


希望这能有所帮助。如果不行,请告诉我。

确保在测试输入后运行设置代码(使用进程隔离)是有帮助的。具体来说,现在可以使用setUp函数实现此功能。是否有任何方法可以使用setUpBeforeClass函数使其工作,因为配置相当昂贵,并且(在实际系统中)每个测试套件包含大量测试。 - borrible
使用进程隔离时的问题在于 setUpBeforeClass() 将在每个测试之前都被调用(正如PHPUnit开发人员告诉我的那样,我之前不知道),所以这有点不起作用。 :( - edorian
似乎setUpBeforeClass也没有被隔离。 - borrible
我遇到了类似的问题。我没有使用define(),而是使用了两个文件,如constant1.php1constant2.php,它们都声明了相同的函数get_config()。结果相同:每个单元测试单独运行时都完美运行,但当全部一起运行时,PHPUnit会抱怨get_config()被声明了两次!@#$ ^%| $)和)@%^ * $)#@) 现在,我想知道是否有更好的设计模式来处理多个测试用例的配置。 - Frederic Bazin

1
我遇到了类似的问题,并找到了一种不同的方法来解决它,可能在最初提出这个问题的时候还没有这种方法。
在我的情况下,我必须测试一些遗留代码,该代码检查全局常量并执行某些操作,如果设置了该常量,则执行该操作。那很容易,但是在一个测试中定义了常量后,它会持续到剩余的测试中。为了改变这种行为,PHPUnit提供了@runInSeparateProcess@preserveGlobalState注释。
/**
 * @runInSeparateProcess
 * @preserveGlobalState disabled
 */
public function test_presence_of_constant() {
  define('SOME_OVERRIDE', 'true');
  $result = $this->target->legacyMethod();
  $this->assertEquals('thereWasSomeOverride', $result);
}

如果您只需要运行少量独立测试,则此方法效果很好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接