首先,我认为对于如何对递归函数进行白盒和黑盒测试没有一个单一的既定定义,但这是我对它的解释。
白盒测试。我们想根据其内部工作来测试该功能。在递归函数的情况下,我认为这意味着我们想要测试它所做的递归调用是否是我们所期望的。一种方法是记录所有递归调用。一个简单的实现gcd
就是添加一个参数来保存日志并返回结果:
let rec gcd log m n =
let log = (m, n)::log
if (m % n) = 0 then List.rev log, n
else gcd log n (m % n)
现在,对于某些两个参数,比如 54 和 22,您可以手动进行计算,确定递归调用的参数应该是什么并为此编写测试:
let log, res = gcd [] 54 22
log |> shouldEqual [ (54, 22); (22, 10); (10, 2) ]
黑盒测试。在这里,我们假设我们不知道该函数究竟是如何工作的,因此我们无法测试其内部结构。我们所能做的就是使用一些输入来测试它。考虑极端情况或棘手的输入可能是一个好主意,因为这些可能会导致问题。给定一个简单的实现:
let rec gcd m n =
if (m % n) = 0 then n
else gcd n (m % n)
我可能会为以下内容编写测试:
// A random case where one of the numbers is the result
gcd 100 50 |> shouldEqual 50
gcd 50 100 |> shouldEqual 50
// A random case where the only divisor is 1
gcd 13 123 |> shouldEqual 1
gcd 123 13 |> shouldEqual 1
// The following are problematic and I'm not sure what the right behaviour is
gcd 0 0 // This probably should not be allowed
gcd 10 -5 // This returns -5, but I'm not sure that's what we want
随机测试。
您还可以使用随机测试(这是一种黑盒测试)来自动生成多个测试用例。我能想到的至少有两个随机测试:
生成两个随机数,a
然后b
检查gcd a b = gcd b a
. 这只是测试一个非常基本的属性,但它可以涵盖相当多的情况。
选择一个随机数a
和几个素数p1, p2, ...
。然后将素数分成两组并产生a*p1*p3*p5
和a*p2*p4*p6
。编写一个测试,检查这两个数字的 GCD 是否为a
。