我正在使用Parsec组合器编写一个解析器库,并且我想对一些解析器进行单元测试。所以我有一个简单的解析器:
dash :: GenParser Char st Char
dash = char '-'
我想为此编写一些测试。积极的测试非常容易:
spec :: Spec
spec = do
describe "dash" $ do
it "parses a dash" $
parse dash "N/A" "-" `shouldBe` (Right '-')
我也想写一个否定的测试。解析器不匹配时,它将返回Left
中的ParseError
。我想编写一个测试来验证ParseError
包含的确切消息。所以我真正想做的是像
spec :: Spec
spec = do
describe "dash" $ do
it "doesn't parse an underscore" $
parse dash "N/A" "_" `shouldSatisfy` (hasErrorMessage "not a dash")
hasErrorMessage (Left (ParseError _ msgs)) expected = msg == expected
hasErrorMessage _ expected = False
但是我无法编写这种代码,因为ParseError
的数据构造函数不是从Text.Parsec.Error
导出的。
在没有适用于该类型的数据构造函数的类型上,是否可以使用模式匹配?
我知道我可以写hasErrorMessage
之类的
hasErrorMessage :: String -> (Either ParseError a) -> Bool
hasErrorMessage expected (Left pe) = elem expected $ fmap messageString (errorMessages pe)
但是我也想了解这个细微差别。
答案 0 :(得分:3)
尽管未导出数据构造函数,但用于访问其参数的函数却已导出。您可以将它们与view patterns结合使用以获取所需的内容。对于您而言,模式(errorMessages -> msgs)
可以完美地代表(ParseError _ msgs)
,但有两个警告:
{-# LANGUAGE ViewPatterns #-}
才能使用此功能。errorMessages
对消息进行排序,而数据构造函数上的模式匹配则不会。您甚至可以将此技术与pattern synonyms结合使用,以创建伪造的数据构造函数,因此可以使用与其他语法完全相同的语法:
{-# LANGUAGE PatternSynonyms, ViewPatterns #-}
pattern ParseError pos msgs <- ((,) <$> errorPos <*> errorMessages -> (pos, msgs)) where
ParseError pos msgs = foldr addErrorMessage (newErrorUnknown pos) msgs