Which is better for testing pure functions?

I am new to Haskell. I am testing a simple function using 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 ] ] 

My concern is that he recommended testing pure functions with QuickCheck properties and unclean functions with HUnit test HUnit . But in this case, I will just need to repeat the definition of the function for each of the three cases ( Const , Variable and Polymorphic ) in the properties to check whether the function returns what it should have. Too much duplication, in my opinion:

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

(etc. for all cases of Kind )

In contrast, in the current code I test only one “obvious” property of a function and provide some “sampling points” for what the function should return, without actually duplicating the definition (in the spirit of unit testing).

What is the right approach?

  • Use properties to test all aspects of this function and possibly duplicate its definition in tests
  • Use properties only for, well, the “properties” of what should be returned, but not duplicate the definition and provide only some unit tests
+4
source share
2 answers

This property-based testing for clean code and unit tests for dirty code is a useful guide, but not an absolute truth. Unit tests can also be useful for clean code. I usually start with unit test, e.g.

 describe "parseMarkdown" $ do it "parses links" $ do parseMarkdown "[foo](http://foo.com/)" `shouldBe` Link "http://foo.com" "foo" 

and then abstract it to property

  it "parses *arbitrary* links" $ property $ \ link@ (Link url name) -> parseMarkdown "[" ++ name ++ "](" ++ url ++ ")" `shouldBe` link 

But sometimes I just stick to unit test, because either (a) there is no good property, or (b) the property does not increase testing coverage.

On the other hand, properties can also be useful for unclean code. You, for example, may want to test the abstraction of your database using properties

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

No, of course, you should not duplicate the definition in this way. What's the point? You can also simplify the test to prop_trivial qk = calculate qk == calculate qk . The only time I thought that this is when you plan to change the way the 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 simply placing the values ​​in the function definition and seeing what happens, they are also not particularly useful for the same reason.

0
source

Source: https://habr.com/ru/post/1501164/


All Articles