Unit testing 测试纯函数的更好方法是什么?

Unit testing 测试纯函数的更好方法是什么?,unit-testing,testing,haskell,Unit Testing,Testing,Haskell,我是哈斯克尔的新手。我正在用Test.Framework测试一个简单的函数: import Test.Framework (defaultMain, testGroup) import Test.Framework.Providers.HUnit import Test.Framework.Providers.QuickCheck2 (testProperty) import Test.QuickCheck import Test.HUnit data Kind = Variable

我是哈斯克尔的新手。我正在用
Test.Framework
测试一个简单的函数:

import Test.Framework (defaultMain, testGroup)
import Test.Framework.Providers.HUnit
import Test.Framework.Providers.QuickCheck2 (testProperty)

import Test.QuickCheck
import Test.HUnit

data Kind = Variable
          | Const
          | Polymorphic
    deriving (Show, Eq, Ord)

calculate :: Int -> Kind -> Float

calculate quantity Variable =
    (**2) . fromIntegral $ quantity

calculate _ Const =
    10

calculate quantity Polymorphic =
    if quantity <= 10 then
        10
    else
        (**2) . fromIntegral $ quantity

prop_ValuePositive quantity kind =
    calculate quantity kind
 >= 0.0

test_ValueVariable1 =
    calculate 1 Variable
    @?= (**2) 1

test_ValueVariable2 =
    calculate 10 Variable
    @?= (**2) 10

test_ValueConst1 =
    calculate 1 Const
    @?= 10

test_ValueConst2 =
    calculate 10 Const
    @?= 10

test_ValuePolymorphic1 =
    calculate 1 Polymorphic
    @?= 10

test_ValuePolymorphic2 =
    calculate 11 Polymorphic
    @?= (**2) 11

instance Test.QuickCheck.Arbitrary Kind where
    arbitrary = Test.QuickCheck.oneof(
        [return Variable,
         return Const,
         return Polymorphic])

main = defaultMain tests

tests = [
    testGroup "Value" [
        testProperty "Value is positive" prop_ValuePositive,
        testCase "Value is calculated right for Variable"
            test_ValueVariable1,
        testCase "Value is calculated right for Variable"
            test_ValueVariable2,
        testCase "Value is calculated right for Const"
            test_ValueConst1,
        testCase "Value is calculated right for Const"
            test_ValueConst2,
        testCase "Value is calculated right for Polymorphic"
            test_ValuePolymorphic1,
        testCase "Value is calculated right for Polymorphic"
            test_ValuePolymorphic2
        ]
    ]
(对于
种类的所有情况,依此类推

相反,在当前代码中,我只测试函数的一个“明显”属性,并为函数应该返回的内容提供一些“样本点”,而不实际复制定义(本着单元测试的精神)

什么是正确的方法

  • 使用属性测试此函数的所有方面,并可能在测试中复制其定义
  • 只对应该返回的内容使用属性,但是不要重复定义,只提供一些单元测试

  • 不,当然你不应该以这种方式复制定义。重点是什么?您还可以将测试简化为
    prop_琐碎的qk=calculateqk==calculateqk
    。唯一的情况是,当你计划改变方法时,在将来计算函数,并检查它仍然返回相同的结果。
    但是,如果您的单元测试是通过将值放入函数定义并查看结果来创建的,那么出于同样的原因,它们也不是特别有用。

    基于属性的测试适用于纯代码,而针对非纯代码的单元测试是有用的指南,但不是绝对真理。单元测试对于纯代码也很有用。我通常从单元测试开始,例如

    describe "parseMarkdown" $ do
      it "parses links" $ do
        parseMarkdown "[foo](http://foo.com/)" `shouldBe` Link "http://foo.com" "foo"
    
    然后将其抽象为一个属性

      it "parses *arbitrary* links" $
        property $ \link@(Link url name) ->
          parseMarkdown "[" ++ name ++ "](" ++ url ++ ")" `shouldBe` link
    
    但有时我坚持使用单元测试,因为(a)没有好的属性,或者(b)属性没有增加测试覆盖率

    另一方面,属性也可以用于不纯代码。例如,您可能希望使用属性测试您的数据库抽象

    describe "loadUser" $ do
      it "retrieves saved users from the database" $ do
        property $ \user -> do
          saveUser user >>= loadUser `shouldReturn` user
    

    我并没有“计划”去改变它的计算方式,但是当你重构代码并且有某种bug潜入时,测试的目的不是为了打破吗?我也不太明白你第二段的意思。那么什么测试是有用的?定义测试的语法是什么?找不到
    descripe
    函数的说明(原文如此!)
    describe "loadUser" $ do
      it "retrieves saved users from the database" $ do
        property $ \user -> do
          saveUser user >>= loadUser `shouldReturn` user