如何使用hspec测试readerError函数是否已执行

时间:2016-06-14 15:54:05

标签: haskell testing hspec

我是Haskell的新手。

我编写了以下代码来解析发送给脚本的参数;

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”。在花了几天阅读monad和读者monad后,我仍然坚持(并且困惑)并且不知道如何测试它以及是否甚至可以测试它。我用Google搜索时找不到任何相关的例子。

1 个答案:

答案 0 :(得分:2)

以下是一些相关类型的信息:

parseDb :: String -> ReadM String

-- from Options.Applicative.Internal

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

runReadMrunP的文档:(link)

ParserPrefs只是一个简单的数据结构。

这个类型检查:

 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'")

<强>更新

以下是如何测试Parser globalOptsParser

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是否符合要求 - 它们会影响选项处理。