Reputation: 3701
I'm new to Haskell. I'm testing a simple function with 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
]
]
What bothers me is that it's recommended to test pure functions with QuickCheck
properties and impure functions with HUnit
test cases. But that way, I would have to just repeat the function definition for each of 3 cases (Const
, Variable
and Polymorphic
) in properties to test that the function returns what it's supposed to. That is too much duplication in my opinion:
prop_ValueVariable quantity Variable =
calculate quantity Variable
== ((**2) . fromIntegral $ quantity)
(and so on for all the cases of Kind
)
In contrast, in the current code I test only one "obvious" property of function and provide some "sample points" for what the function should return, without actually duplicating the definition (in spirit of unit testing).
What is right approach?
Upvotes: 4
Views: 574
Reputation: 421
That property based testing is for pure code and unit tests for impure code is a useful guideline, but not an absolute truth. Unit tests can be useful for pure code, too. I usually start with a unit test, e.g.
describe "parseMarkdown" $ do
it "parses links" $ do
parseMarkdown "[foo](http://foo.com/)" `shouldBe` Link "http://foo.com" "foo"
and then later abstract it to a property
it "parses *arbitrary* links" $
property $ \link@(Link url name) ->
parseMarkdown "[" ++ name ++ "](" ++ url ++ ")" `shouldBe` link
But sometimes I just stick with the unit test because either (a) there is no good property or (b) a property does not increase the test coverage.
On the other hand properties can be useful for impure code, too. You e.g. may want to test your database abstraction with properties
describe "loadUser" $ do
it "retrieves saved users from the database" $ do
property $ \user -> do
saveUser user >>= loadUser `shouldReturn` user
Upvotes: 3
Reputation: 170839
No, of course you are not supposed to duplicate the definition in this way. What would be the point? You may as well simplify the test to prop_trivial q k = calculate q k == calculate q k
. The only case when I'd consider it is when you plan to change the way function is calculated in the future and want to check that it still returns the same result.
But if your unit tests are created by just putting values into the function definition and seeing what comes out, they are also not particularly useful either, for the same reason.
Upvotes: 0