Phpunit: 使 TestHook 接口像 TestListener 一样易于使用

创建于 2018-11-05  ·  23评论  ·  资料来源: sebastianbergmann/phpunit

这个问题是在#3381 中开始的讨论应该继续的地方。

与#3388 和#3389 相关。

eveneu-foss2019-10

最有用的评论

在去年的过程中,我们了解到虽然使用TestHook接口实现的方法比我们之前使用TestListener接口实现的方法要好,但它仍然不够好。 @theseer@localheinz正在研究一种新的解决方案,该解决方案将于今年晚些时候集成。 届时, TestHook接口以及与它们相关的所有内容都将被弃用并计划删除。

我知道我们弃用了旧的TestListener系统,引入了TestHook系统,它不像它打算替换的系统那样易于使用,这一定令人沮丧,现在我们正在谈论弃用TestHook系统以及引入另一个新系统。 对此我真的很抱歉,但我确信@theseer@localheinz正在开发的基于事件的系统将弥补这一点。

所有23条评论

一些想法:

  1. TestHook目前不提供任何类似于 TestListener::startTestSuite 和 TestListener::endTestSuite 的功能,但TeamCityJUnit使用这些功能,我没有看到一个方法绕过它。 所以我们可能需要一个BeforeTestSuiteHookAfterTestSuiteHook

  2. 就 BC 而言,使用钩子来实现当前使用 TestListener 的所有类可能是最简单的,因此 PHPUnit 本身可以使用新扩展,而用户空间可以继续扩展 TestListeners,至少在 PHPUnit 9 之前,TestListener 和所有实现它将被删除。 这确实意味着 e2e 测试中不再覆盖旧类。 不确定这是否是件坏事,因为 PHPUnit 本身并没有使用它们?

  3. printerClass设置可以保持不变,但我们可以检查 TestRunner 是否是 TestListener 或 Extension,并相应地注册它。 当它是 TestListener 时,我们还可以打印一条弃用消息(在 8.0 中)。

我只是尝试了一个快速的概念验证来使 TeamCity 记录器成为一个扩展,目前这是不可能的。

首先,printResult 方法(将成为executeAfterLastTest 方法)需要一个TestResult,但没有得到。 将此添加到界面将是一个 BC 中断。

然后 addError (我相信它将成为 executeAfterFailedTest)需要访问实际的 Throwable 以获取堆栈跟踪,但只接收一个消息字符串。

在那之后我停止了尝试,但我相信会有更多的问题同样徒劳。

@rpkamp感谢您的详细反馈! 我记录了在为 #3380 重构 TestDox 打印机时遇到的问题,我对TestHook的所需更改列表与您的非常相似。

我会写出更详细的回应和建议。 底线与 Remon 所说的相同:当前的打印机/侦听器获得了有关测试运行的更多详细信息。 startTest()endTest()的等价物只是:

interface BeforeTestHook extends TestHook
{
    public function executeBeforeTest(string $test): void;
}

interface AfterSuccessfulTestHook extends TestHook
{
    public function executeAfterSuccessfulTest(string $test, float $time): void;
}

重新实现当前的内置输出记录器需要(更多)可用信息。

只是一个快速提示:例如,我查看了johnkary/phpunit-speedtrap并尝试从实现已弃用的TestListener接口切换到实现(或多或少)相应的钩子。

现在我注意到以前通过实现TestListener可以访问实际的TestCase并调用它的方法 - 在这种特殊情况下,检查来自测试方法的注释,这些注释可能提供有关个人门槛。

当切换到似乎不再可能的钩子时(至少不是以简单的方式),因为现在传递的是测试名称而不是TestCase 。 也就是说,只要可以很容易地从测试名称派生测试类和测试方法,那应该不是问题,否则使用钩子实现行为会有点困难。

你怎么认为? 测试名称能否成为确定测试类和方法的可靠来源?

我想用TestHook接口替换TestListener接口的原因有很多。 以下是我的一些想法:

  • TestListener方法太多,违反接口隔离原则
  • TestListener传递实际的TestCase对象,不仅允许读(“听”)访问,还允许写访问

小的TestHook接口允许在不破坏向后兼容性的情况下添加新的钩子功能(向TestListener添加新方法是向后兼容性中断)。

此外,遵循最小可见性原则, TestHook实现者目前只收到最少的信息。 如果我们发现需要更多信息,那么我们可以添加它。 请注意,此信息的添加可能会破坏向后兼容性,因此必须等待 PHPUnit 的下一个主要版本。 但请注意,我不想传递TestCase 。 多年来,我收到了太多令人困惑的错误报告,这些报告是由于“侦听器”弄乱了TestCase对象。 一个解决方案可能是为我们传递的TestCase创建一个只读代理,而不是真正的TestCase对象。 但即便如此,我也想避免。

@sebastianbergmann如果使用钩子而不是侦听器,您如何从字符串访问测试类?

你不。 你不应该需要。

在 PHPStorm 通知我PHPUnit\Framework\TestListener已被弃用后,我通过谷歌搜索发现了这个线程。 发生了什么? 8.2 中是否有这些新的“钩子”? 8.2 的文档仍然大量谈论使用 TestListeners,尽管它们显然已被弃用。
https://phpunit.readthedocs.io/en/8.2/extending-phpunit.html#implement -phpunit-framework-testlistener

TestHook接口已经存在一段时间了。 它们 1) 尚未记录和 2) 还不容易(足够)使用。

我试图理解它们 - 主要是因为我需要连接到 'beforeFirstTest' 事件,而不是 'startTestSuite' 事件(无论运行了多少套件,我都需要在开始时执行一个操作,然后执行一个操作)最后),而年长的听众似乎没有那个功能。 据我所知,它们需要添加为 .phar 扩展名,而不是通过 phpunit.xml <listeners>元素加载? 这是额外的困难吗?

不,您可以将它们作为扩展安装在 phpunit.xml 中:

<extensions>
    <extension class="Namespace\To\Your\Listener" />
</extensions>

只要确保你的类实现了PHPUnit\Runner\TestHook

不错,谢谢! 很抱歉有些偏离主题的分歧。

如果测试套件包含特定的@group我有一个监听器会做一些事情。

class MyListener implements TestListener {
    public function startTestSuite(TestSuite $suite): void
    {
        // check $suite->getGroups() and do some stuff
    }
}

如何将其转换为基于 TestHook 的扩展?
我尝试转换,但缺少BeforeTestSuiteHook
顺便说一句, @epdenouden我们去年已经讨论过了

添加用例...

我正在添加一个自定义注释,我在测试之前/之后使用 TestListener 通过$test->getAnnotations()读取 - 能够使用钩子来执行此操作(即具有可用注释)会很好。

对于symfony/phpunit-bridge这是一个很大的 BC-break 大多数功能在https://github.com/symfony/symfony/blob/4.4/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.phphttps 中实现: //github.com/symfony/symfony/blob/4.4/src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php似乎很难/不可能迁移到新的 API。

我找到了一种获取注释的方法,但我不能说它感觉正确:

use PHPUnit\Util\Test;

Test::parseTestMethodAnnotations(...explode('::', $test))

@karser ,您可能可以使用与Test::getGroups类似的东西

但要注意: * <strong i="15">@internal</strong> This class is not covered by the backward compatibility promise for PHPUnit

@greg0ire @nicolas-grekas 让我们在 11 月 23 日的 SymfonyCon 阿姆斯特丹 Hackday 上亲自讨论symfony/phpunit-bridge

@greg0ire @nicolas-grekas 让我们在 11 月 23 日的 SymfonyCon 阿姆斯特丹 Hackday 上亲自讨论symfony/phpunit-bridge

@greg0ire @nicolas-grekas @sebastianbergmann我很乐意加入,我会带 _stroopwafels_

在 EU FOSSA 黑客马拉松期间,我们为记录器的新事件处理系统完成了大量工作。 我很乐意帮助实现新的事件系统,以重新创建桥所需的功能,并在需要的地方进行扩展。

我们需要游说 https://github.com/ManoManoTech,以便@greg0ire可以参加 SFCon :)

现在是正式的,我要走了! 所以无论你在幕后做了什么游说,它都奏效了👍

今天我花了一个小时试图为一个简单的场景找到一个可接受的解决方案。

我有一个“集成”套件需要初始化我正在集成的库,还有一个“单元”套件只测试我的库。 初始化集成很慢(涉及数据库),所以我需要避免在不必要的时候这样做。

正如其他人所说,钩子的一个大问题是没有一个钩子听套件开始/结束。

但这还不是全部。

通常,尤其是在开发过程中,我使用--filter有选择地运行测试,我需要知道我过滤的内容是否属于集成套件。

以当前的钩子状态,这是不可能的,我认为解决这个问题的一种方法是TestHook知道配置对象

钩子无法_修改_配置(数组按值传递)或做监听器可以做的讨厌的事情接收TestCase ,但同时可以为钩子提供足够的上下文来决定是否/如何表现。

事实上,对于我的用例,我采取的做法是使用BeforeFirstTestHook::executeBeforeFirstTest并查看命令行选项(查看$argv ),搜索--filter和/或--testsuite选项。
由于我的集成测试类位于Tests\Integration命名空间中,因此我可以在最终过滤器中测试stripos($filter, 'integration') === false

请注意,解析选项并不容易,因为需要同时考虑--filter Foo--filter=Foo语法。

如果钩子对象知道配置对象,我可以写 3 行代码而不是 25 行。

也许ConfigAwareHook与接口withRunnerConfigs(array $configs)可以做的伎俩:任何Hook实现它,实例化之后,亚军可以调用该方法经过配置,它会完全向后兼容。

我打开了一个单独的问题,但它可能与这个有关:#4131

基本上,我想要--printer的替代方法,这是一种无需编辑配置即可添加此挂钩的方法。

TestHook接口已经存在一段时间了。 它们 1) 尚未记录和 2) 还不容易(足够)使用。

@sebastianbergmann如果它们不够容易使用,那么它们不应该被标记为@deprecated ,不是吗? 当然,这不是一个重大问题,但它会在运行混乱的测试时出现一堆不需要的弃用警告。 我知道这些注释已经存在一段时间了,因为这个线程已经快 2 年了。 在 TestHook 实现更容易集成之前,我们可以删除@deprecated注释吗?

在去年的过程中,我们了解到虽然使用TestHook接口实现的方法比我们之前使用TestListener接口实现的方法要好,但它仍然不够好。 @theseer@localheinz正在研究一种新的解决方案,该解决方案将于今年晚些时候集成。 届时, TestHook接口以及与它们相关的所有内容都将被弃用并计划删除。

我知道我们弃用了旧的TestListener系统,引入了TestHook系统,它不像它打算替换的系统那样易于使用,这一定令人沮丧,现在我们正在谈论弃用TestHook系统以及引入另一个新系统。 对此我真的很抱歉,但我确信@theseer@localheinz正在开发的基于事件的系统将弥补这一点。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

rentalhost picture rentalhost  ·  4评论

joubertredrat picture joubertredrat  ·  4评论

AnmSaiful picture AnmSaiful  ·  4评论

greg0ire picture greg0ire  ·  4评论

TiMESPLiNTER picture TiMESPLiNTER  ·  3评论