Haskell Feature Flag Demo (Part 4)
This entry of the Haskell Feature Flag Demo series presents tests for the demo using simple types as well as the demo using tagged feature flag configuration.
Spec
The Spec
module defines the main function for the test,
using the tasty
test framework. It is straightforward except that it sets the
TASTY_NUM_THREADS
environment variable to ensure that the
tests are executed sequentially. This is essential because the tested
code uses global storage. Tests use different feature flag
configuration, and tests running in parallel would conflict.
main :: IO ()
= do
main "TASTY_NUM_THREADS" "1" -- run only one test at a time!
setEnv $ testGroup "test"
defaultMain
[ Demo1.IO.Test.tests
, Demo2.IO.Test.tests ]
Demo1.IO.Test
This module implements tests for the foo
and
bar
pure functions as well as the run
function
of the demo
using simple types.
Function foo
is tested using unit tests, passing
different feature flag configuration for each test.
testFoo :: TestTree
= testGroup "foo"
testFoo "-ffSomeBug" $ 13 @=? Demo1.IO.foo False
[ testCase "+ffSomeBug" $ 42 @=? Demo1.IO.foo True
, testCase ]
Function bar
is tested using property testing. The
property asserts that the refactored implementation is equivalent to the
original implementation.
testBar :: TestTree
= testProperty "bar" prop_equiv
testBar where
prop_equiv :: Int -> Bool
= Demo1.IO.bar False n == Demo1.IO.bar True n prop_equiv n
Function run
is tested using unit tests, passing
different combinations of feature flag configurations for each test.
testRun :: TestTree
= testGroup "run"
testRun "-ffSomeBug -ffSomeBusinessLogic" $
[ testCase 26 @=?) =<< Demo1.IO.run (ffMap False False)
("-ffSomeBug +ffSomeBusinessLogic" $
, testCase 26 @=?) =<< Demo1.IO.run (ffMap False True)
("+ffSomeBug -ffSomeBusinessLogic" $
, testCase 84 @=?) =<< Demo1.IO.run (ffMap True False)
("+ffSomeBug +ffSomeBusinessLogic" $
, testCase 84 @=?) =<< Demo1.IO.run (ffMap True True)
(
]where
ffMap :: Bool -> Bool -> FF.FeatureFlagMap
= Map.fromList
ffMap ffSomeBug ffSomeBusinessLogic FF.Fix_202407_SomeBug_42, ffSomeBug)
[ (FF.Ref_202407_SomeBusinessLogic, ffSomeBusinessLogic)
, ( ]
Demo2.IO.Test
This module implements tests for the foo
and
bar
pure functions as well as the run
function
of the demo
using tagged feature flag configuration.
Function foo
is tested using unit tests, passing
different feature flag configuration for each test. Helper function
ffSomeBug
constructs a feature flag configuration of the
correct type. The default case is tested as well.
testFoo :: TestTree
= testGroup "foo"
testFoo "-ffSomeBug" $ 13 @=? Demo2.IO.foo (ffSomeBug (Just False))
[ testCase "+ffSomeBug" $ 42 @=? Demo2.IO.foo (ffSomeBug (Just True))
, testCase "!ffSomeBug" $ 42 @=? Demo2.IO.foo (ffSomeBug Nothing)
, testCase
]where
ffSomeBug :: Maybe Bool -> FF.FeatureFlagConfig FF.Fix_202407_SomeBug_42
= FF.FeatureFlagConfig ffSomeBug
Function bar
is tested using property testing. The
property asserts that the refactored implementation is equivalent to the
original implementation. Note that the ffSomeBusinessLogic
helper function hard-codes the Just
constructor because the
case of no configuration is irrelevant to this property.
testBar :: TestTree
= testProperty "bar" prop_equiv
testBar where
prop_equiv :: Int -> Bool
=
prop_equiv n False) n
Demo2.IO.bar (ffSomeBusinessLogic == Demo2.IO.bar (ffSomeBusinessLogic True) n
ffSomeBusinessLogic :: Bool
-> FF.FeatureFlagConfig FF.Ref_202407_SomeBusinessLogic
= FF.FeatureFlagConfig . Just ffSomeBusinessLogic
Function run
is tested using unit tests, passing
different combinations of feature flag configurations for each test.
testRun :: TestTree
= testGroup "run"
testRun "-ffSomeBug -ffSomeBusinessLogic" $
[ testCase 26 @=?) =<< Demo2.IO.run (ffMap False False)
("-ffSomeBug +ffSomeBusinessLogic" $
, testCase 26 @=?) =<< Demo2.IO.run (ffMap False True)
("+ffSomeBug -ffSomeBusinessLogic" $
, testCase 84 @=?) =<< Demo2.IO.run (ffMap True False)
("+ffSomeBug +ffSomeBusinessLogic" $
, testCase 84 @=?) =<< Demo2.IO.run (ffMap True True)
(
]where
ffMap :: Bool -> Bool -> FF.FeatureFlagMap
= DMap.fromList
ffMap ffSomeBug ffSomeBusinessLogic FF.Fix_202407_SomeBug_42 ==> ffSomeBug
[ FF.Ref_202407_SomeBusinessLogic ==> ffSomeBusinessLogic
, ]
Code
The full source is available on GitHub.
These tests uses GHC 9.6.5 and can be run using the following command.
$ cabal test
Alternatively, if you use Stack, run the demo using the following command.
$ stack test