...
tests:
Test4-test:
main: Spec.hs
source-dirs: test
ghc-options:
- -threaded
- -rtsopts
- -with-rtsopts=-N
dependencies:
- Test4
- tasty
- tasty-quickcheck
- tasty-hunit
Folder: 'project/test'
In folder test is a haskell file as root for all tests name spec.hs.
spec.hs:import qualified Test.Tasty as T
import qualified SpecModuleA -- test module for module ModuleA
import qualified SpecModuleB -- test module for module ModuleB
import qualified SpecModuleC -- test module for module ModuleC
main :: IO ()
main =
T.defaultMain tests
tests :: T.TestTree
tests =
T.testGroup
"Tests"
[
SpecModuleA.testGroup,
SpecModuleB.testGroup,
SpecModuleC.testGroup
]
spec<Module>.hs:module SpecCode
(
testGroup
) where
import qualified Test.Tasty as T
import qualified Test.Tasty.QuickCheck as QC
import qualified Test.Tasty.HUnit as HU
import qualified ModuleA as A
-- ...
spec<Module>.hs:-- ...
testGroup :: T.TestTree
testGroup =
T.testGroup
"module A"
[
tgClassA,
tgUnitf1,
tgUnitf2,
tgUnitf3
]
-- ...
tgClassA :: T.TestTree
tgClassA =
T.testGroup
"class A"
[
-- laws:
tgLawA,
tgLawB,
tgLawC,
-- unit tests:
tgUnitf3,
tgUnitlcdConvert
]
-- ...
spec<Module>.hs:-- ...
tgLawA :: T.TestTree
tgLawA =
T.testGroup
"Law: A"
[
-- ...,
HU.testCase "#u1" (A.f4 3 HU.@?= (A.f5 3 * A.f5 3)),
HU.testCase "#u2" (A.f4 4 HU.@?= (A.f5 4 * A.f5 4)) -- ...
]
-- ...
spec<Module>.hs:-- ...
tgLawA :: T.TestTree
tgLawA =
T.testGroup
"Law: A"
[
-- ...,
QC.testProperty "#p1" (\nj -> A.f3 nj QC.==> (A.f4 nj == (A.f5 nj * A.f5 nj)) ),
QC.testProperty "#p2" (\nj -> not A.f3 nj QC.==> (A.f4 nj == -1 ) -- ...
]
-- ...
QuickCheck tests with test data of random samples, and the frequency distribution of the ramdom samples can be controlled by the class Arbitrary.
data TooHugeToTestAll = ToHuge { ra :: Int, rb :: Int, rc :: Int, rd :: Int }
deriving (Show)
instance QC.Arbitrary TooHugeToTestAll where
arbitrary = ToHuge <$> f <*> f <*> f <*> f
where
f = QC.frequency
[
(1, QC.choose (niMin, -1)), -- -1 or less
(5, QC.choose (0, niMax)), -- 0 or more
(3, QC.choose (niMaxPlus1, 1000)), -- up to 1000
(1, QC.choose (1001, maxBound :: Int)) -- up to maxBound
]
niMax :: Int
niMax = 3
niMaxPlus1 :: Int
niMaxPlus1 = niMax + 1
niMin :: Int
niMin = - niMax
Background: The following instances already exist with a standard distribution for sample data.
Problem: So, how to control them differently?
Solution: By declaration of a new type (by 'newtype').
newtype Char' = Char' Char
deriving Show
fromChar' :: Char' -> Char
fromChar' (Char' ch) = ch
instance QC.Arbitrary Char' where
arbitrary =
do
genChar <- QC.frequency
[
(1, QC.elements ['A'..'Z']),
(1, QC.elements ['a'..'z']),
(1, QC.elements ['0'..'9']),
(5, QC.chooseAny )
]
return (Char' genChar)
-- ...
QC.testProperty "Char #p1" (\ch' -> (fromChar' (ch' :: Char') == '\0') QC.==> (fXYZ (fromChar' ch')) ),
{-# LANGUAGE FlexibleInstances #-}
-- ...
newtype T' a = T' a
deriving Show
fromT' :: T' a -> a
fromT' (T' x) = x
instance QC.Arbitrary (T' Char) where
arbitrary =
do
genChar <- QC.frequency
[
(1, QC.elements ['A'..'Z']),
(1, QC.elements ['a'..'z']),
(1, QC.elements ['0'..'9']),
(5, QC.chooseAny )
]
return (T' genChar)
-- ...
QC.testProperty "Char #p1" (\cd' -> (fromT' (cd' :: (TH.T' Char)) == '\0') QC.==> (fXYZ (fromT' cd') == '\0') ),
The following symbols are possible for each validation criteria:
NOTE: correct functioning depends on "Cd.cdFromChar :: Char -> CharUtf8" test mitigation: ✅ supplemental test data (approx. 100 characters) are use in unit tests
Each function has its own test group designated as tgUnit<functionName>.
And each law has its own test group designated as tgLaw<LawName>.
Tests are validated by checking the following criteria:
After checking the above mentioned criteria, the result is documented as source code comment:
{- * validated: ✅
* completeness: ✅
* independence: ✅
* edge cases : ✅
* conform doc.: ✅ -}
tgUnitf1 :: T.TestTree
tgUnitf1 =
T.testGroup
"f1"
[
-- ...
]
Each function has its own test group designated as tgClass<ClassName>.
Tests are validated by checking the following criteria:
After checking the abovementioned criteria, the result is documented as source code comment:
{- * validated: ✅
* documented: ✅
* laws : ✅
* characteristics : ✅
* prefix : ✅
* short description: ✅
* completeness: ✅
* all laws documented : ✅
* all type combinations : ✅
* stubbed for default functions: ✅ -}
tgClassA :: T.TestTree
tgClassA =
T.testGroup
"class A"
[
-- laws:
tgLawA,
tgLawB,
tgLawC,
-- unit tests:
tgUnitf3,
tgUnitlcdConvert
]
Each module exports a test group designated as testGroup.
Tests are validated by checking the following criteria:
Description : provides a class for ... Copyright : (c) <Author>, <Year> License : <License> Maintainer : <E-Mail-Address> Stability : experimental Portability : POSIX
{- * validated: ✅
* documented: ✅
* completeness: ✅ -}
testGroup :: T.TestTree
testGroup =
T.testGroup
"module A"
[
tgClassA,
tgUnitf1,
tgUnitf2,
tgUnitf3
]