2016-06-14 55 views
1

我是Haskell的新手。如何用hspec測試readerError函數是否執行

我寫了下面這段代碼,它將解析發送給腳本的參數;

module Billing.Options 
    (
     GlobalOpts(..) 
    , globalOptsParser 
    , parseDb 
) where 

import Options.Applicative 
import Options.Applicative.Simple 
import Options.Applicative.Types 
import System.FilePath.Posix 
import Text.Regex.PCRE 

-- ------------------------------------------------------------ 

data GlobalOpts = GlobalOpts 
    { 
    optDb   :: String, 
    optSql   :: String 
    } deriving Show 

-- ------------------------------------------------------------ 

globalOptsParser :: Parser GlobalOpts 
globalOptsParser = GlobalOpts 
    <$> option (parseDb =<< readerAsk) 
    ( long "db" 
    <> short 'd' 
    <> metavar "<DB name>" 
    <> help "dmt | report" 
    ) 
    <*> option parseSql 
    ( long "sql" 
    <> metavar "<SQL SELECT statement>" 
    <> help "sql select statement to use in order to generate JSON config file" 
    ) 
-- ------------------------------------------------------------ 

matches :: String -> String -> Bool 
matches = (=~) 

-- ------------------------------------------------------------ 

parseDb :: String -> ReadM String 
parseDb val = do 
    if not (elem val ["dmt", "report"]) 
     then readerError $ "Unknown DB, '" ++ val ++ "'" 
     else return val 

-- ------------------------------------------------------------ 

parseSql :: ReadM String 
parseSql = do 
    val <- readerAsk 
    if not (val `matches` "(?i)select .+ from .+") 
     then readerError $ "Please provide a valid SQL SELECT statement" 
     else return val 

-- [EOF] 

我想用hspec測試上面的「parseDb」函數。我想確保在指定未知數據庫時會拋出「readerError」。因此,我想測試函數調用 parseDb「未知」 生成一個「readerError」調用,根據我應該拋出一個異常。

我試過了hspec shouldThrow函數,但它不起作用。似乎沒有例外被拋出。 readerError的返回類型是「ReadM a」。花了幾天的時間閱讀monads和閱讀器monads後,我仍然陷入困境(並且困惑不已),並且不知道如何測試它以及是否可以測試它。我在Google上搜索時找不到任何相關示例。

+0

用另一個例子更新了我的答案。 – ErikR

回答

2

這裏有一些相關的類型SIGS:

parseDb :: String -> ReadM String 

-- from Options.Applicative.Internal 

runReadM :: MonadP m => ReadM a -> String -> m a 
runP :: P a -> ParserPrefs -> (Either ParseError a, Context) 

Google文檔runReadMrunP(link)

ParserPrefs只是一個簡單的數據結構。

這typechecks:

import Options.Applicative.Types 
import Options.Applicative.Internal 

parseDb :: String -> ReadM String 
parseDb val = do 
    if not (elem val ["dmt", "report"]) 
     then readerError $ "Unknown DB, '" ++ val ++ "'" 
     else return val 

foo :: (Either ParseError String, Context)  -- might be [Context] now 
foo = runP (runReadM (parseDb "foo") "asd") opts 
    where opts = ParserPrefs "suffix" False False False 80 

評估fst foo回報:

*Main> fst foo 
Left (ErrorMsg "Unknown DB, 'foo'") 

更新

下面是如何測試ParserglobalOptsParser

import Options.Applicative.Common (runParser) 
import Options.Applicative.Internal (runP) 

bar = let mp = runParser AllowOpts globalOptsParser ["asd"] 
      opts = ParserPrefs "suffix" False False False 80 
     in fst $ runP mp opts 

這裏["asd"]是要測試的命令行參數。

檢查ParserPrefs是否是你想要的 - 它們可以影響選項處理。