单元测试应该复制功能还是测试输出?

Dan*_*ley 17 ruby testing tdd unit-testing

我已多次遇到这种困境.我的单元测试是否应该复制他们正在测试的方法的功能以验证其完整性?或者单元测试是否应该尝试使用大量手动创建的输入和预期输出实例来测试方法?

我主要询问的问题是,您测试的方法相当简单,并且可以通过查看代码一分钟来验证其正确的操作.

简化示例(在ruby中):

def concat_strings(str1, str2)
  return str1 + " AND " + str2
end
Run Code Online (Sandbox Code Playgroud)

上述方法的简化功能复制测试:

def test_concat_strings
  10.times do
    str1 = random_string_generator
    str2 = random_string_generator
    assert_equal (str1 + " AND " + str2), concat_strings(str1, str2)
  end
end
Run Code Online (Sandbox Code Playgroud)

我知道大多数时候你测试的方法都不够简单,不能用这种方式来证明这一点.但我的问题仍然存在; 在某些情况下这是一种有效的方法(为什么或为什么不这样做)

Jen*_*der 9

通过使用相同的实现来测试功能,不会测试任何东西.如果其中有一个错误,另一个也会.

但通过与替代实施方案进行比较测试是一种有效的方法.例如,您可以通过将斐波纳契数字与同一方法的简单递归但缓慢的实现进行比较来测试计算斐波纳契数的迭代(快速)方法.

其中一个变体是使用一种实现,它只适用于特殊情况.当然,在这种情况下,您只能将它用于此类特殊情况.

选择输入值时,大多数时候使用随机值不是很有效.我希望随时都能选择精心挑选的价值观.在你给出的例子中,我想到了连接时不符合字符串的空值和非常长的值.

如果使用随机值,请确保您可以使用相同的随机值重新创建精确运行,例如通过记录种子值,以及在开始时设置该值的方法.


Mar*_*ann 5

这是一个有争议的立场,但我相信使用派生值进行单元测试远远优于使用任意硬编码输入和输出。

问题是,当算法变得稍微复杂时,如果用硬编码值表示,输入和输出之间的关系就会变得模糊。单元测试最终成为一个假设。它可能在技术上有效,但会损害测试的可维护性,因为它会导致Obscure Tests

使用派生值对结果进行测试在测试输入和预期输出之间建立了更清晰的关系

认为这不测试任何东西的论点根本不正确,因为任何测试用例都只会执行通过 SUT 的路径的一部分,因此没有单个测试用例会重现被测试的整个算法,但测试组合可以所以。

另一个好处是您可以使用更少的单元测试来覆盖所需的功能,甚至同时使它们更具交流性。最终结果是更简洁、更易于维护的单元测试。