首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Haskell Hspec/Quickcheck数字函数测试

Haskell Hspec/Quickcheck数字函数测试
EN

Code Review用户
提问于 2020-06-30 10:11:13
回答 1查看 82关注 0票数 3

我正在学习使用HSpec和QuickCheck。例如,我正在实现来自维基百科:扩展的欧几里德算法的伪代码。您可以在github找到用于实现测试代码的项目。

我特别想知道有两种做法:

  • 测试用例的选择--我从维基百科页面中选取了两个简单的示例,并进行了三个属性测试。
  • 在我看来,a>0 && b>0的效率很低。

我最感兴趣的是什么是一个很好的实践来确认两种算法产生相同的结果。

代码语言:javascript
复制
module EuclidSpec  ( spec )
where

import Test.Hspec
import Test.Hspec.Core.QuickCheck
import Test.QuickCheck
import Lib

spec :: Spec
spec = do
  describe "Trivial" $ do
    it "trivial example 99 1" $
       let trivial = extendedEuclid 99 1
       in  trivial `shouldBe` (EuclidRes 1 (0) 1)
    it "trivial example 99 99" $
       let trivial = extendedEuclid 99 99
       in  trivial `shouldBe` (EuclidRes 99 (0) 1)
  describe "Examples" $ do
    it "explanation example 99 78" $
       let wikiExample = extendedEuclid 99 78
       in  wikiExample `shouldBe` (EuclidRes 3 (-11) 14)
    it "explanation example flipped 78 99" $
       let wikiExample = extendedEuclid 78 99
       in  wikiExample `shouldBe` (EuclidRes 3 14 (-11) )
    it "explanation example 99 78" $
       let wikiExample = extendedEuclid 240 46
       in  wikiExample `shouldBe` (EuclidRes 2 (-9) 47)
  describe "properties" $ do
      it "both numbers divisible a%gcd == 0, b%gcd ==0" $ property $
            prop_divisible
      it "bezout a*s+b*t = gcd" $ property $
            prop_bezout
      it "recursive and iterative algorithm have same result" $ property $
            prop_same_as_recursive

prop_divisible a b = a>0 && b>0 ==> a `mod` d ==0 && b `mod`d == 0
  where EuclidRes d s t = extendedEuclid a b
                             
prop_bezout a b = a>0 && b>0 ==> a*s + b*t == d
  where EuclidRes d s t = extendedEuclid a b

prop_same_as_recursive a b = a>0 && b>0 ==> extendedEuclid a b == extendedEuclid' a b
EN

回答 1

Code Review用户

发布于 2020-06-30 12:15:43

啊,一个好的Spec。我已经有一段时间没有使用Hspec了,但是您的测试似乎是合理的。所以,首先:well完成了!

但是,有一点我们应该修复,您已经自己识别了它:属性测试。

QuickCheck的新型

创建任何类型的数字,然后检查它是否为正数是一个麻烦,因为一半的数字将被每个候选人丢弃。但是,由于Hspec使用QuickCheck,所以我们只能使用Positive生成正数:

代码语言:javascript
复制
prop_divisible (Positive a) (Positive b) =  a `mod` d == 0 && b `mod`d == 0
  where EuclidRes d s t = extendedEuclid a b

除此之外,没有比这更客观的改进。

然而,有一些个人我会使用在我自己的规格。

减少规范

中的let … in …绑定

考虑下面的规范

代码语言:javascript
复制
  describe "Trivial" $ do
    it "trivial example 99 1" $
       let trivial = extendedEuclid 99 1      
       in  trivial `shouldBe` (EuclidRes 1 (0) 1)

如果我想理解规范,我必须阅读第一行,记住trivial的值(并且它在调用extendedEuclid后没有被更改),并在下一行中提供它。

如果我改写

代码语言:javascript
复制
  describe "Trivial" $ do
    it "trivial example 99 1" $
       extendedEuclid 99  1 `shouldBe` (EuclidRes 1 (0) 1)
-- or
    it "trivial example 99 99" $
       extendedEuclid 99 99 
           `shouldBe` (EuclidRes 99 (0) 1)

我立即看到extendedEucild正在接受测试。这也是符合官方风格,其中let … in …绑定根本不使用

其他小片

您可以使用来自Test.Hspec.QuickCheckD22而不是it "..." $ property $ ...

代码语言:javascript
复制
import Test.Hspec.QuickCheck

...

  describe "properties" $ do
      prop "both numbers divisible a%gcd == 0, b%gcd ==0" $ 
            prop_divisible
      ...
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/244774

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档