首页 > 解决方案 > 当结果是随机的时运行 rspec 测试两次

问题描述

我有一个算法可以在 99% 的置信度下工作。所以如果我设置这样的测试

let(:valid_input_signal) { randomly_generate_signal_plus_noise }
it { expect(my_detector(valid_input_signal).signal_present?).to be true }

它会失败 1% 的时间。我可以编写一个测试,在数千个有效输入信号上运行 my_detector 并检查它只有 1% 的时间失败,但这需要很长时间才能运行,并且测试的目的不是检查算法是否有效,而是代码中没有任何破坏算法的更改。

我认为实现此目的的有效方法是再次运行上述测试,如果通过,则通过。如果它第二次失败,那么就给它一个失败,因为假设基础是正确的,它连续失败两次的机会是万分之一。当然,这意味着 10,000 次组合测试将在有效代码库上失败,但这比目前的情况要好得多,目前 100 次测试失败中有 1 次失败。

那么rspec有没有办法做到这一点,即如果第一次失败则再次运行测试,并且仅在第二次失败时才输出失败?

标签: rspec

解决方案


并且测试的目的不是检查算法是否有效,而是没有更改破坏算法的代码。

您无法在随机生成的输入数据中对其进行测试。假设您有一组 100 个 valid_input_signals,它在第 5 个失败,没关系。有人改变了算法,它在 5 号开始通过,在 17 号失败。在 1% 的情况下它仍然失败,但它仍然正确吗?

如何从这里继续并不明显——这取决于算法的作用。也许您可以将一些部分提取到单一职责组件中并单独测试它们?

但也许它是一种遗留算法,你需要尽可能地用规范来覆盖它?如果是这样 - 我会用速度换取覆盖范围:

生成 1000 个信号,并保存那些my_detector(valid_input_signal).signal_present? == true,将它们保存在文件或其他东西中,并针对这些确定性输入运行规范。

valid_inputs.each do |input| 
  expect(my_detector(input).signal_present?).to be true
end

假设其中 10 个生成的 give false,检查并确认它是一个真正的否定(他们假设返回 false),将它们保存在其他地方并为它们制作规格:

invalid_inputs.each do |input| 
  expect(my_detector(input).signal_present?).to be false
end

假设(在手动检查之后)其中只有 8 个是真阴性,剩下的 2 个应该返回 true,但返回 false - 这些可能是错误。保存它们以备后用。

您对 1000 个样本的覆盖范围感到满意吗?运行需要多长时间?您可以交换一些时间并将覆盖范围增加到 10000 个样本吗?百万?这是你的选择。

在某些时候,添加更多样本是没有意义的。现在您已经大致了解了整个算法,您可以开始一些基本的重构,例如提取方法命名魔法常量并提取逻辑组件(在此答案的第 2 部分中提到)。X 个样本测试是临时的,它们确保(尽可能多地使用非无限样本大小)整个算法的行为不会改变,而您会凿掉您更好理解的部分。


推荐阅读