什么用于属性测试

时间:2015-05-16 10:31:00

标签: unit-testing haskell quickcheck property-based-testing

我想知道什么是属性测试目标,它的甜点是什么,应该在哪里使用。让'有一个我想测试的示例函数:

f :: [Integer] -> [Integer]

这个函数f获取一个数字列表,并对奇数进行平方并滤除偶数。我可以说明一些关于函数的属性,比如

      
  1. 给出偶数列表,返回空列表。
  2.   
  3. 给定一个奇数列表,结果列表的大小与输入相同。
  4.   
  5. 鉴于我有一个偶数列表和一个奇数列表,当我加入它们时,随机播放并传递给函数,结果的长度将是奇数列表的长度。
  6.   
  7. 鉴于我提供了一个正奇数列表,那么同一索引的结果列表中的每个元素都将大于原始列表
  8.   
  9. 鉴于我提供了一个奇数和偶数的列表,加入并随机播放它们,然后我会得到一个列表,其中每个数字都是奇数
  10.   
  11. 没有属性测试,该函数适用于最简单的情况,例如我可以做一个简单的案例,如果我错误地实现了f,它将传递这些属性:

    f = fmap (+2) . filter odd
    

    所以,如果我想介绍一些简单的情况,看起来我需要在属性规范中重复算法的基本部分,或者我需要使用基于值的测试。重复算法的第一个选项可能是有用的,如果我打算改进算法,如果我计划改变它的实现,例如速度。通过这种方式,我有一个参考实现,我可以用来再次测试。

    如果我想检查一下,算法并不会因某些琐碎的情况而失败,而且我不想在规范中重复算法,看起来我需要进行一些单元测试。我会写例如这些检查:

    f ([2,5]) == [25]
    f (-8,-3,11,1) == [9,121,1]
    

    现在我对算法更有信心了。

    我的问题是,基于属性的测试是否意味着要取代单元测试,还是它是互补的?是否有一些一般性的想法,如何编写属性,所以它们是有用的还是完全取决于对函数逻辑的理解?我的意思是,能否说出以某种方式写出属性特别有益?

    此外,是否应该努力使属性测试算法的每个部分?我可以把算法放在一边,然后在其他地方进行测试,让属性测试它看起来像的过滤部分,它可以很好地覆盖它。

    f :: (Integer -> Integer) -> [Integer] -> [Integer]
    f g = fmap g . filter odd
    

    然后我只能传递Prelude.id并使用单元测试在其他地方测试g

2 个答案:

答案 0 :(得分:4)

以下属性如何:

  1. 对于源列表中的所有奇数,其square是结果列表的元素。
  2. 对于结果列表中的所有数字,源列表中有一个数字,其正方形为。
  3. 顺便说一下,odd\x -> x % 2 == 1

    更容易阅读

答案 1 :(得分:3)

参考算法

对此进行(可能效率低下的)参考实现和测试是很常见的。实际上,这是实现数值算法时最常见的快速检查策略之一。但并非算法的每个部分都需要一个。有时会有一些属性完全表征算法。 在这方面,Ingo的评论是明确的:这些属性决定了算法的结果(最多订购和重复)。要恢复订单和重复项,您可以修改属性以包含“在源元素位置后截断的结果列表中”,反之亦然。

测试的粒度

当然,考虑到Haskell的可组合性,单独测试算法的每个合理的小部分是很好的。我相信,例如\x -> x*xfilter odd作为参考,无需查看两次。

是否应该为每个部分提供属性并不像以后那样明确算法部分,从而使属性没有实际意义。由于Haskell的懒惰不是常见的事情,但它确实发生了。