简介。
不久前,我们正在开发一个代码,该代码将一些数据存储在给定的TTL中。我们不仅要检查数据的存储正确,还要检查给定TTL之后的数据是否过期。这是测试异步代码的一个示例。
测试异步代码时,我们需要与系统仔细协调测试,以避免在测试操作完成之前运行断言。例如,以下测试将始终失败,因为在数据过期之前检查了第30行中的断言:
在这种情况下,测试总是失败,但在其他情况下,系统可能会在系统工作时间歇性失败,或者在系统破裂时通过。我们需要进行测试等待,以便我们测试的时间成功完成,如果在给定的超时期内没有发生这种情况。
睡眠不是最好的选择。
这是先前测试的改进版本,在检查数据已过期之前,我们要等待测试代码,以便将测试时间的代码运行:
简单的睡眠方法的问题是,在某些运行中,超时可能足以使数据过期,但在其他运行中可能不会,因此测试会间歇性地失败。它成为一项闪烁的测试。闪烁的测试令人困惑,因为当它们失败时,我们不知道这是由于真正的错误,还是只是假阳性。如果失败相对常见,团队可能会开始忽略那些可以掩盖真实缺陷并完全破坏具有自动测试的值的测试。
由于间歇性失败的发生,因为超时离我们正在测试所需的行为所花费的时间太近,因此许多团队决定通过增加每个测试的时间在检查测试中的操作之前,都会增加每个测试的时间来减少这些失败的频率那是成功的。这是不切实际的,因为它很快会导致运行时间太长的测试套件。
替代方法。
如果我们能够更快地检测成功,那么成功的测试将提供快速的反馈,我们只需要等待测试失败的超时即可。这是一种比等待每个测试的时间都要好得多,无论其失败或成功。
有两种主要检测成功的主要策略:捕获通知 [2]和对变化的投票。。
在我们以作为例子为例的情况下,轮询是唯一的选择,因为Redis没有发送我们可以听的任何监视事件。
更改的轮询。
为了尽快检测成功,我们将通过时间间隔进行几次探测,该时间间隔将比以前的超时时间短。如果探测的结果是我们期望的测试通行证,那么如果我们期望的结果还没有,我们就会睡觉并重试。如果经过几次重试,则不存在预期值,测试将失败。
在以下代码中查看checkThatDataHasExpired
方法:
通过对更改进行轮询,我们避免始终等待最长时间的时间。只有在最坏的情况下,当消耗所有试验而没有检测到成功的情况下,我们将等待使用固定超时的正义睡眠方法。
提取辅助器。
散射临时低级代码,在整个测试中进行了像checkThatDataHasExpired
一样进行轮询和探针,这不仅使它们难以理解,而且是重复的非常糟糕的情况。因此,我们将其提取给助手,因此我们可以在不同的情况下重复使用。
与,检查,失败之前的探针数量 和探针之间的时间,我们提取到以下helper[3]:
这是使用辅助器后先前的测试的样子:
请注意,我们将探针,检查,探针数和探针之间的睡眠时间传递给AsyncTestHelpers::assertWithPolling
函数。
结论。
我们在PHP中展示了一种测试Steve Freeman和Nat Pryce在Growing Object-Oriented Software, Guided by Tests书中描述的异步代码的方法。这种方法避免了闪烁的测试,并且比使用固定超时的测试套件更快。我们还通过提取在代码中重复使用的辅助功能来展示如何抽象这种方法。
我们希望您发现这种方法很有趣。如果您想了解有关此的更多信息以及其他几种技术来有效测试异步代码,请查看精彩的Growing Object-Oriented Software, Guided by Tests book[4]。
致谢。
感谢我的Codesai同事阅读了最初的草稿,并给了我反馈和Chrisy Totty的可爱猫图片。
注释。
[1]这是定时(cot)的连续性的一个很好的例子。当执行多个组件的时间很重要时,就会发生COT。在这种情况下,要测试的动作必须在断言检查其可观察效果之前进行。那就是我们谈论的协调。检查our post about Connascence以了解有关此有趣主题的更多信息。
[2]在捕获通知策略中,测试代码“通过聆听系统发送的事件来观察系统。基于事件的断言会通过阻止显示器,直到收到通知或达到时间来等待事件。 ,(摘自Growing Object-Oriented Software, Guided by Tests book)。
前一段时间,我们开发了some helpers using the capturing notifications strategy 来测试使用core.async通道的异步clojurescript代码。例如,查看 Expect-Async-Message
断言助手,其中我们在其中使用 core.asenc/alts!
和 core.async/timeout
以实现此行为。 core.async/alts!
函数选择第一个响应的通道。如果该渠道是测试代码正在观察的一个渠道,我们断言收到的消息是我们期望的。如果首先响应的频道是由 core.asenc/timeout
生成的频道,我们将使测试失败。我们在上一篇文章中提到了这些 async-test-tools
:Testing Om components with cljs-react-test。
[3]在GOOS Code examples repository中查看testing asynchronous systems examples,以便针对更改策略进行调查的助手实施更面向对象的助手,并示例捕获通知策略的示例。
[4]第27章,测试异步代码,包含对有效测试异步代码的两种主要策略的很好的解释:捕获更改的通知和投票。