【问题标题】:PHPUnit - Automatically retry failed tests X times?PHPUnit - 自动重试失败的测试 X 次?
【发布时间】:2011-10-09 17:22:10
【问题描述】:

我有一组复杂的 PHPUnit 测试,其中一些涉及连接到世界各地的服务器,无论出于何种原因,这些服务器有时会超时。

我不想在服务器超时时让测试失败,我只想在实际将其标记为失败之前重试该测试一次或多次。

现在,我明白这可能不是处理我手头情况的最佳方式。一种更好的解决方案是修复服务器。但是,现在这超出了我的控制范围。

所以,我真正想要的是一种告诉 PHPUnit 重新测试每个失败的测试用例 X 次的方法,并且只有在每次都失败时才将其标记为失败。

有什么想法吗?

编辑:你们中的许多人都提出了我不这样做的有用建议。我明白了,谢谢。但是,具体而言,我要做的是创建一个测试套件,用于测试完整系统的操作,包括远程服务器。我了解测试我的代码的某些部分的概念来自外部的“模拟”响应......但如果我的部分测试测试“全栈”,我晚上也会睡得更好。

【问题讨论】:

  • 根据您的编辑: 1. 当您说“它不是您的主机”时,您会说“它不是 您的 系统的一部分”。因此,您不应该将它包含在您的测试中,因为无论如何您都无法影响它,并且它会伪造结果。 2. 你永远不会创建测试$this->assertEquals(1, rand(0,1)),对吗?当您包含不确定的外部系统时,就会发生这种情况。

标签: php phpunit integration-testing


【解决方案1】:

由于 PHPUnit 不支持这种开箱即用的行为,您需要自己编写循环代码。不要在需要它的每个测试中都这样做,而是创建一个自定义测试用例基类(如果您还没有),它扩展 PHPUnit_Framework_TestCase 并提供该功能。

您可以花哨并覆盖 testBare() 以检查诸如 @retry 5 之类的注释,循环该次数,调用 parent::testBare(),并吞下除最后一个以外的所有异常(或子集)。

public function runBare() {
    // I'll leave this part to you. PHPUnit supplies methods for parsing annotations.
    $retryCount = $this->getNumberOfRetries();
    for ($i = 0; $i < $retryCount; $i++) {
        try {
            parent::runBare();
            return;
        }
        catch (Exception $e) {
            // last one thrown below
        }
    }
    if ($e) {
        throw $e;
    }
}

或者您可以创建一个类似的辅助方法,将重试次数和闭包/可调用作为参数,并从需要它的每个测试中调用它。

public function retryTest($count, $test) {
    // just like above without checking the annotation
    ...
        $test();
    ...
}

public function testLogin() {
    $this->retryTest(5, function() {
        $service = new LoginService();
        ...
    });
}

【讨论】:

  • 嗨,David,您能否提供更多详细信息,如果测试用例失败,我必须重试一次。
  • 感谢大卫的回复。如果在不使用任何注释的情况下失败,我想简单地重试我的测试用例。我做了什么..我使用 $retryCount = 2 在我的基类中放入了运行裸函数的代码[如您的答案中所定义],此后我不清楚:(
  • @Deep123 将$retryCount 设置为常数,您就可以开始了。以上runBare 和$retryCount = 2 就是您所需要的。如果现在失败,您的测试方法不会运行两次吗?在 Gist 或其他内容中发布完整的测试用例和基类。
  • 另请注意,示例代码还会重试跳过/未完成的测试。您可能希望捕获并重新抛出这些,以避免不必要地重试。
  • 基于这个答案gist.github.com/makasim/989fcaa6da8ff579f7914d973e68280c的完整解决方案@
【解决方案2】:

不完全是您问题的答案,但我还是会说:您的测试永远不应该包含远程资源(尤其是当它们完全不受您控制时(与本地镜像不同))。您应该将您的连接封装到单独的类中(例如Connection),并在您的测试中模拟这些对象并使用静态响应,您的远程主机将返回这些响应。

【讨论】:

  • 单元测试确实如此,但对于自动化验收测试,关于是否模拟外部服务仍有很多争论。我们选择不这样做,它会增加测试的脆弱性。在这种情况下,重试可能会有所帮助。
【解决方案3】:

您不应该使用模拟对象和固定装置而不是连接到实时服务器,以便其他地方的响应不会影响您的测试吗?

您可以使用依赖注入来使用特定的 HTTP 客户端,该客户端将返回您告诉它的数据和响应代码(取决于您的代码的编写方式)。理想情况下,您的单元测试应该独立于外部影响;你应该控制你正在测试的内容,例如,强制 404 或 500 错误应该是你测试的一个单独部分。

与其试图破解非确定性测试,不如看看是否可以更改代码以启用模拟和测试装置。

除此之外,您当然可能已经知道,恐怕我不知道有什么方法可以告诉 PHPUnit 允许测试失败。这似乎与该工具应该做的完全相反。

【讨论】:

  • +1,重要的最后一句:“这似乎与工具应该做的完全相反。”
  • 如果你在前端测试中模拟浏览器,你就错了。
【解决方案4】:

我认为没有支持,有人可以证明我错了,但在这种情况下我会感到惊讶。

我认为您可以做的不是在要检索的测试方法中立即断言而是循环 X 次并在成功时中断,在循环之外,结果将被断言。

您已经考虑过的这种简单方法的缺点是向每个测试方法添加更多代码。如果你有很多,它会增加维护负担。

否则,如果您想实现更多自动化,您可以实现PHPUnit_Framework_TestListener,在关联数组中保留失败测试的计数并与测试运行进行比较。不确定这条路线是否可行,但您可以尝试一下。

【讨论】:

    【解决方案5】:

    您应该能够创建一个数据库连接断言并在您的其他测试中“作为”您的数据库连接来实现该测试。在该测试中,您可以根据需要尝试多次,并在 X 次尝试后返回 false。

    【讨论】:

      猜你喜欢
      • 2015-10-31
      • 2013-05-04
      • 2013-07-28
      • 2011-10-30
      • 2022-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-26
      相关资源
      最近更新 更多