我设法让xUnit处理我的小样本组装。现在我想知道我是否也可以找到FsCheck。我的问题是,在为我的函数定义测试属性时,我感到很难过。
也许我只是没有一套好的样本函数,但是这些函数的测试属性是什么,例如?
//transforms [1;2;3;4] into [(1,2);(3,4)]
pairs : 'a list -> ('a * 'a) list //'
//splits list into list of lists when predicate returns
// true for adjacent elements
splitOn : ('a -> 'a -> bool) -> 'a list -> 'a list list
//returns true if snd is bigger
sndBigger : ('a * 'a) -> bool (requires comparison)
答案 0 :(得分:14)
已经有很多具体的答案,所以我会尝试给出一些可能会给你一些想法的一般答案。
作为一般建议,尽量不要做太大的交易。对于sndBigger来说,一个好的财产将是:
让``当且仅当snd更大时才应该返回``(a:int)(b:int)= sndBigger(a,b)= b>一个
这可能就是实施。不要担心 - 有时一个简单的,老式的单元测试正是你需要的。无需内疚! :)
也许this link(Pex团队)也提出了一些想法。
答案 1 :(得分:10)
我将从sndBigger
开始 - 这是一个非常简单的函数,但是您可以编写一些应该保留它的属性。例如,当您反转元组中的值时会发生什么:
// Reversing values of the tuple negates the result
let swap (a, b) = (b, a)
let prop_sndBiggerSwap x =
sndBigger x = not (sndBigger (swap x))
// If two elements of the tuple are same, it should give 'false'
let prop_sndBiggerEq a =
sndBigger (a, a) = false
编辑:此规则prop_sndBiggerSwap
并不总是成立(请参阅 kvb 的评论)。但是以下内容应该是正确的:
// Reversing values of the tuple negates the result
let prop_sndBiggerSwap a b =
if a <> b then
let x = (a, b)
sndBigger x = not (sndBigger (swap x))
关于pairs
功能, kvb 已经发布了一些好主意。此外,您可以检查将转换后的列表转换回元素列表会返回原始列表(当输入列表为奇数时,您需要处理这种情况 - 取决于pairs
函数应该执行的操作这种情况):
let prop_pairsEq (x:_ list) =
if (x.Length%2 = 0) then
x |> pairs |> List.collect (fun (a, b) -> [a; b]) = x
else true
对于splitOn
,我们可以测试类似的东西 - 如果你连接所有返回的列表,它应该给出原始列表(这不会验证分裂行为,但是开始时是一件好事 - 它至少保证不会丢失任何元素)。
let prop_splitOnEq f x =
x |> splitOn f |> List.concat = x
我不确定 FsCheck 是否可以处理这个(!),因为该属性将函数作为参数(因此需要生成“随机函数”)。如果这不起作用,则需要使用一些手写函数f
提供一些更具体的属性。接下来,对分割列表中的所有相邻对({ kvb 建议)实施f
返回true的检查实际上并不困难:
let prop_splitOnAdjacentTrue f x =
x |> splitOn f
|> List.forall (fun l ->
l |> Seq.pairwise
|> Seq.forall (fun (a, b) -> f a b))
当你从一个列表中给出最后一个元素并从下一个列表中给出第一个元素时,你可以检查的唯一最后一件事是f
返回false
。以下内容尚未完全完成,但它显示了可行的方法:
let prop_splitOnOtherFalse f x =
x |> splitOn f
|> Seq.pairwise
|> Seq.forall (fun (a, b) -> lastElement a = firstElement b)
最后一个示例还显示您应该检查splitOn
函数是否可以返回空列表作为返回结果列表的一部分(因为在这种情况下,您找不到第一个/最后一个元素)。
答案 2 :(得分:7)
对于某些代码(例如sndBigger
),实现非常简单,任何属性都至少与原始代码一样复杂,因此通过FsCheck进行测试可能没有意义。但是,对于其他两个函数,这里有一些你可以检查的东西:
pairs
List.map fst (pairs x) = evenEntries x
和List.map snd (pairs x) = oddEntries x
表示您可以编写的简单函数evenEntries
和oddEntries
。splitOn
splitOn f l
的结果中的每个列表,没有两个连续的条目满足f”和“Take lists {{ 1}}来自(l1,l2)
成对,splitOn f l
成立“。不幸的是,这里的逻辑可能在复杂性上与实现本身相当。