我已经设法得到 x单位 正在组装我的小样品。现在我想看看我是否可以理解 安全检查 也。我的问题是,在为我的函数定义测试属性时,我感到很困惑。

也许我只是没有得到一组好的函数示例,但是例如,这些函数的良好测试属性是什么?

//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)

没有正确的解决方案

其他提示

已经有很多具体的答案,所以我将尝试给出一些一般性的答案,这可能会给您一些想法。

  1. 递归函数的归纳性质。对于简单的函数,这可能相当于重新实现递归。不过,保持简单:而实际的实施往往会不断发展(例如它变成尾递归,你添加记忆,...)保持属性简单。==> 属性组合器通常在这里派上用场。你的pairs函数可能就是一个很好的例子。
  2. 包含模块或类型中多个函数的属性。检查抽象数据类型时通常会出现这种情况。例如:向数组添加一个元素意味着该数组包含该元素。这会检查 Array.add 和 Array.contains 的一致性。
  3. 往返行程:这有利于转化(例如解析、序列化) - 生成任意表示,对其进行序列化、反序列化,检查它是否等于原始表示。您可以使用 splitOn 和 concat 来完成此操作。
  4. 作为健全性检查的一般属性。寻找可能成立的众所周知的属性——例如交换性、结合性、幂等性(应用两次不会改变结果)、自反性等。这里的想法更多的是稍微练习一下这个函数——看看它是否做了什么真正奇怪的事情。

作为一般性建议,尽量不要小题大做。对于 sndBigger 来说,一个好的属性是:

让``当且仅在snd更大时返回tru

这可能就是具体的实现。不要担心 - 有时一个简单的、老式的单元测试正是您所需要的。无需内疚!:)

或许 这个链接 (Pex 团队)也给出了一些想法。

我会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。接着,执行检查f返回在分割后列出了所有相邻的一对真实的(如 KVB 建议)实际上不是那么困难:

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功能是否可以返回一个空列表作为结果返回列表的一部分(因为在这种情况下,你无法找到第一个/最后一个元素)。

对于某些代码(例如 sndBigger),实现非常简单,任何属性都至少与原始代码一样复杂,因此通过 FsCheck 进行测试可能没有意义。但是,对于其他两个函数,您可以检查以下一些内容:

  • pairs
    • 当原始长度不能被二整除时,期望什么?您可以检查是否抛出异常,如果这是正确的行为。
    • List.map fst (pairs x) = evenEntries xList.map snd (pairs x) = oddEntries x 对于简单的功能 evenEntriesoddEntries 你可以写。
  • splitOn
    • 如果我理解您对该函数应该如何工作的描述,那么您可以检查诸如“对于结果中的每个列表”之类的条件 splitOn f l, ,没有两个连续条目满足 f" 和 "获取列表 (l1,l2)splitOn f l 成对地, f (last l1) (first l2) 持有”。不幸的是,这里的逻辑在复杂性上可能与实现本身相当。
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top