【问题标题】:What to use property testing for属性测试用于什么
【发布时间】:2015-07-28 06:22:21
【问题描述】:

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

f :: [Integer] -> [Integer]

这个函数,f,接受一个数字列表,并将奇数平方并过滤掉偶数。我可以说明该函数的一些属性,例如

  1. 给定一个偶数列表,返回空列表。
  2. 给定一个奇数列表,结果列表将具有与输入相同的大小。
  3. 鉴于我有一个偶数列表和一个奇数列表,当我加入它们时,随机播放并传递给函数,结果的长度将是奇数列表的长度。
  4. 假设我提供了一个正奇数列表,那么结果列表中相同索引处的每个元素都将大于原始列表中的元素
  5. 鉴于我提供了一个奇数和偶数列表,将它们加入并随机播放,然后我将得到一个列表,其中每个数字都是奇数

没有任何属性测试,该函数适用于最简单的情况,例如我可以做一个简单的案例,如果我错误地实现了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

【问题讨论】:

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


    【解决方案1】:

    以下属性如何:

    1. 对于源列表中的所有奇数,其平方是结果列表的元素。
    2. 对于结果列表中的所有数字,源列表中都有一个数字与其平方。

    顺便说一句,odd\x -> x % 2 == 1 更容易阅读

    【讨论】:

      【解决方案2】:

      参考算法

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

      测试的粒度

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

      每个部分是否应该有属性并不像您稍后可能内联算法的该部分那样清楚,从而使属性没有实际意义。由于 Haskell 的懒惰,这并不常见,但它确实发生了。

      【讨论】:

        猜你喜欢
        • 2016-12-06
        • 2014-05-07
        • 2015-06-24
        • 2020-04-12
        • 1970-01-01
        • 2011-04-24
        • 2016-03-01
        • 2016-10-09
        • 1970-01-01
        相关资源
        最近更新 更多