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

How to test with hspec whether the readerError function was executed

我是 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"。因此我想测试函数调用 解析数据库 "unknown" 生成一个 "readerError" 调用,根据我的说法应该抛出异常。

我试过 hspec shouldThrow 函数,但它不起作用。似乎没有抛出异常。 reader错误的 return 类型是 "ReadM a"。在花了几天时间阅读 monads 和 reader monads 之后,我仍然卡住了(并且很困惑)并且不知道如何测试它以及是否有可能测试它。我用谷歌搜索时找不到任何相关示例。

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

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 returns:

*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 是否是您想要的 - 它们会影响选项处理。