扩展PHPUnit:添加装饰器

9

背景

最近,我接手了一款程序良好的 PHP 应用程序(讽刺),该应用程序基于商业软件(我不会透露名称),并且有一个我们构建的自定义层。

不幸的是,这个应用程序使用了大量的全局变量和单例模式(意图明显)。我已经为我们覆盖的所有内容构建了测试用例。然而,很多事情依赖于某些全局状态,这可能会导致竞态条件和各种奇怪的问题。

随机化测试

为了捕获大部分这些怪异的问题(我喜欢称之为“怪人级别”),我已经按照[手册中的文档][1]构建了一个 PHPUnit TestDecorator。它是这样的:

class PHPUnit_Extensions_Randomizer extends PHPUnit_Extensions_TestDecorator
{
    public function __construct(PHPUnit_Framework_Test $test)
    {
        $tests = $test->tests();

        $shuffle = array();
        foreach ($tests as $t) {
            if ($t instanceof PHPUnit_Framework_TestSuite) {
                $shuffle = array_merge($shuffle, $t->tests());
            } else {
                $shuffle[] = $t;
            }
        }
        shuffle($shuffle);

        $suite = new PHPUnit_Framework_TestSuite();
        foreach ($shuffle as $t) $suite->addTest($t);

        parent::__construct($suite);
    }
}

该方法的基本作用是随机化测试顺序,以确保测试不依赖于可能正确或不正确的全局状态。

问题

当测试自定义装饰器时,出现了问题。我没有在手册、Google或Stack Overflow中找到如何加载它的任何信息。

在分析代码时,我发现PHPUnit自身在TextUI_TestRunner::doRun()方法中实例化了RepeatedTest装饰器。我知道可以子类化TestRunner,覆盖doRun(),安排创建我的装饰器,然后只需使用我的装饰器实例作为参数调用parent::doRun(),重写TextUI_Command并创建一个新的CLI脚本来使用我的东西而不是内置的东西。

在重新发明轮子之前,我只是想知道:有没有可能在不子类化TestRunner的情况下加载自定义装饰器?

谢谢。

1个回答

3

目前的PHPUnit版本没有简单的方法来插入自己的代码。唯一提供互换性的是TestRunner,你所描述的对我来说是有意义的。

我不知道任何其他方式来提供测试修饰符或更改phpunit使用的大部分其他类。

您想要更改测试执行顺序的方式似乎可以奏效,即使我不确定它是否会打乱套装。


实现这一点的另一种方法可能会更简单,即创建PHPUnit_Framework_TestSuite子类,并随机添加您的代码。


如果这行不通,另一个选择是使用xml配置文件,并从<file>标签构建测试套件,在每次执行之前都有一些代码来洗牌这些标签。据我所知,phpunit没有以任何方式排序它们,因此执行将是随机的。


您是否要查看每个测试是否真正有效,并希望找到相互依赖的测试?

还是您真的想知道当您按错误顺序执行很多不应更改任何内容的操作时是否会出现严重错误?

我问只是为了确保您尚未考虑过--process-isolation。(我认为您已经考虑过了,但询问并不会伤害,反而可能为您节省一些时间和精力:))

当您使用全新的全局设置运行每个测试时,您至少会发现所有测试之间的相互依赖性,只需要一个cli开关即可确保套件中的每个测试都可以正常工作。


谢谢你的回答。是的,我考虑过进程隔离,但速度太慢了(非常非常慢)。我不能承受每次运行测试套件都要等待10分钟的时间。 - netcoder
@netcoder 我建议设置一个构建,每晚运行一次套件,并使用 --process-isolation 来捕获异常情况,在日常使用中正常运行。你不想在测试期间找到随机错误,因为你不希望你的测试套件“随机”出错。我手头上有一个测试套件,使用 process-isolation 运行需要一个小时以上(耶!),每晚运行一次真的缓解了与它一起工作的痛苦。 - edorian
我们还遇到了测试干扰的问题——其中一个测试以某种方式改变全局状态,从而要么a)破坏另一个测试,要么b)隐藏另一个测试中的错误。我希望能够在每次运行时对测试进行洗牌。进程隔离无法找到第一种干扰。 - David Harkness
尽管它没有解决我的问题,但我接受了这个答案,因为它是真实的,PHPUnit没有添加装饰器的功能。覆盖TestRunner和Command是必须的。 - netcoder
@netcoder:我已经按照你在原始问题中描述的方式创建了解决方案,链接在这里https://github.com/fiunchinho/phpunit-randomizer,以防你从未实际实现它。它可以随机化测试用例,而不修改PHPUnit的代码。 - Jose Armesto
显示剩余2条评论

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