我是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
| 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
]
]
让我感到困扰的是,建议使用QuickCheck
属性测试纯函数,并使用HUnit
测试用例测试函数。但是那样,我必须在属性中重复3个案例(Const
,Variable
和Polymorphic
)中每个案例的函数定义,以测试函数返回它应该返回的内容。在我看来,这太重复了:
prop_ValueVariable quantity Variable =
calculate quantity Variable
== ((**2) . fromIntegral $ quantity)
(等Kind
)的所有案例
相反,在当前的代码中,我只测试函数的一个“明显”属性,并为函数返回的内容提供一些“样本点”,而不是实际复制定义(单元测试的精神)。
什么是正确的方法?
答案 0 :(得分:3)
基于属性的测试用于纯代码,不纯代码的单元测试是一个有用的指导,但不是绝对的事实。单元测试也可用于纯代码。我通常从单元测试开始,例如
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
答案 1 :(得分:0)
不,当然你不应该以这种方式复制定义。重点是什么?您也可以将测试简化为prop_trivial q k = calculate q k == calculate q k
。我考虑的唯一情况是你计划在未来计算改变函数的方式,并想检查它是否仍然返回相同的结果。
但是,如果您的单元测试是通过将值放入函数定义并查看结果来创建的,那么它们也不是特别有用,出于同样的原因。