Last active
July 9, 2020 05:36
-
-
Save aerohit/5a4e774cb872a015a8cf0e0c40fd0973 to your computer and use it in GitHub Desktop.
Unit testing Tagless Final
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Reference: https://jproyo.github.io/posts/2019-03-17-tagless-final-haskell.html | |
newtype UserName = UserName String deriving Show | |
newtype DataResult = DataResult String deriving (Show, Eq) | |
newtype AppConfig = AppConfig String deriving Show | |
newtype LogMsg = LogMsg String deriving Show | |
class Monad m => Cache m where | |
getFromCache :: UserName -> m (Maybe DataResult) | |
storeCache :: UserName -> DataResult -> m () | |
class Monad m => DataSource m where | |
getFromSource :: UserName -> m DataResult | |
class Monad m => Logger m where | |
logMsg :: LogMsg -> m () | |
class Monad m => ConfigProvider m where | |
getConfig :: m AppConfig | |
requestData | |
:: (ConfigProvider m, Cache m, DataSource m, Logger m) | |
=> UserName | |
-> m DataResult | |
requestData userName = do | |
cfg <- getConfig | |
logMsg $ LogMsg $ show cfg <> " is read as config" | |
cache <- getFromCache userName | |
case cache of | |
Nothing -> do | |
logMsg $ LogMsg $ show userName <> " not found in cache" | |
result <- getFromSource userName | |
logMsg $ LogMsg $ show userName <> " found in datasource" | |
storeCache userName result | |
return result | |
Just result -> do | |
logMsg $ LogMsg $ show userName <> " found in cache" | |
return result | |
---- IO instances for PRODUCTION | |
instance Cache IO where | |
getFromCache _ = return Nothing | |
storeCache _ _ = return () | |
instance DataSource IO where | |
getFromSource userName = return $ DataResult $ "Data: " <> show userName | |
instance Logger IO where | |
logMsg msg = print $ show msg | |
instance ConfigProvider IO where | |
getConfig = return $ AppConfig "HelloApp" | |
runApp :: IO () | |
runApp = do | |
r <- requestData $ UserName "rohit" | |
print r | |
---- TESTING scenarios | |
-- 1 - data found in cache | |
-- 2 - data not found in cache, but found in data source | |
-- 3 - data not found in cache and not found in data source | |
-- Scenario 1: found in cache | |
newtype InCache a = | |
InCache { unInCache :: Identity a } | |
deriving (Show, Functor, Applicative, Monad) | |
instance Cache InCache where | |
getFromCache _ = InCache $ return $ Just $ DataResult "Data 1" | |
storeCache _ _ = undefined | |
instance DataSource InCache where | |
getFromSource _ = undefined | |
instance ConfigProvider InCache where | |
getConfig = return $ AppConfig "config" | |
instance Logger InCache where | |
logMsg _ = return () | |
testDataInCache :: InCache Bool | |
testDataInCache = do | |
r <- requestData $ UserName "name 1" | |
return $ r == DataResult "Data 1" | |
-- Scenario 2: not found in cache, but found in data source | |
newtype NotInCache a = | |
NotInCache { unNotInCache :: Identity a } | |
deriving (Show, Functor, Applicative, Monad) | |
instance Cache NotInCache where | |
getFromCache _ = NotInCache $ return Nothing | |
storeCache _ _ = return () | |
instance DataSource NotInCache where | |
getFromSource _ = return $ DataResult "Data 2" | |
instance ConfigProvider NotInCache where | |
getConfig = return $ AppConfig "config" | |
instance Logger NotInCache where | |
logMsg _ = return () | |
testDataAbsentInCacheButPresentInDataSource :: NotInCache Bool | |
testDataAbsentInCacheButPresentInDataSource = do | |
r <- requestData $ UserName "name 1" | |
return $ r == DataResult "Data 2" | |
---- TODO: Scenario 3: skipped NeitherFoundInCacheNorDataSource meaning one more type class and four more instances to test this scenario | |
testAll = do | |
test1 <- unInCache testDataInCache | |
test2 <- unNotInCache testDataAbsentInCacheButPresentInDataSource | |
return $ test1 && test2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment