[英]Hunit testing with exceptions
I am writing a program to parse some xml. 我正在编写一个程序来解析一些xml。
I took the approach of using MonadThrow
to take care of errors in parsing, but now when testing the fails - can't figure out how to test them. 我采取了使用
MonadThrow
来处理解析错误的方法,但是现在测试失败时-无法弄清楚如何测试它们。 Which makes me unsure if this approach is the right one. 这使我不确定这种方法是否正确。
First of all here is a complete (non-working) example 首先,这里是一个完整的(无效)示例
exception.hs
{-# LANGUAGE OverloadedStrings #-}
import Test.Tasty
import Test.Tasty.HUnit
import Control.Exception (SomeException, displayException)
import Control.Monad (unless)
import Control.Monad.Trans.Resource (MonadThrow)
import Data.Function (on)
import Text.XML (Element, parseText, def, documentRoot, elementName)
import Data.Text (Text)
import Data.Text.Lazy (fromStrict)
data TestElement = TestElement deriving (Show, Eq)
main :: IO ()
main = defaultMain unitTests
unitTests :: TestTree
unitTests = testGroup "Unit tests"
[ testCase "parseTxt parser goodTxt1 == Right TestElement " $
parseTxt parser goodTxt1 @?= Right TestElement
, testCase "parseTxt parser goodTxt2 == Right TestElement " $
parseTxt parser goodTxt2 @?= Right TestElement
, testCase "parseTxt parser failTxt == Left \"ElementName does not match TestElement\"" $
parseTxt parser failTxt @?= undefined
--hunit
]
parseTxt :: (Element -> Either SomeException a) -> Text -> Either SomeException a
parseTxt parser inText = documentRoot <$> (parseText def $ fromStrict inText) >>=
parser
parser :: MonadThrow m => Element -> m TestElement
parser elmt =
do unless (elementName elmt == "TestElement")
$ fail "ElementName does not match TestElement"
{-here usually some more complicated attribute/subnode parsing happens-}
return TestElement
failTxt :: Text
failTxt = "<ToastElement></ToastElement>"
goodTxt1 :: Text
goodTxt1 = "<TestElement />"
goodTxt2 :: Text
goodTxt2 = "<TestElement></TestElement>"
instance Eq SomeException where
(==) = (==) `on` displayException
which needs exception.cabal
这需要
exception.cabal
[...]
executable exception
hs-source-dirs: src
main-is: Main.hs
default-language: Haskell2010
build-depends: base >= 4.7 && < 5
, xml-conduit
, exceptions
, resourcet
, tasty
, tasty-hunit
, text
I am not sure what to put instead of the undefined
in the last unit test and if the approach of using exceptions is right in this case. 我
undefined
在上一个单元测试中要放置什么而不是undefined
,以及在这种情况下使用异常的方法是否正确。
There are several options I thought of: 我想到了几种选择:
(either displayException show $ parseTxt parser failTxt) @?= undefined
still fails and does not yield a Left
value (either displayException show $ parseTxt parser failTxt) @?= undefined
仍然会失败,并且不会产生Left
值 assertFail
defies the purpose of having a Either SomeException TestElement
in my opinion assertFail
违抗有目的Either SomeException TestElement
在我看来, I think one of the sources of my confusion is that I don't know when the error is thrown (I thought lazy evaluation would throw the error when I matched against it - which is apparently wrong). 我认为我感到困惑的原因之一是我不知道何时引发错误(我认为当我将其与之匹配时,惰性评估会引发错误-显然是错误的)。
Thanks to @user2407038 I've been able to solve this: 感谢@ user2407038,我得以解决此问题:
defining a new datatype for the exception 为异常定义新的数据类型
data ParseException = TagMismatch String deriving (Typeable, Eq, Show)
then adjusting the imports and the following functions 然后调整导入和以下功能
parseTxt :: Exception e => (Element -> Either e a) -> Text -> Either SomeException a
parseTxt parser inText = documentRoot <$> (parseText def $ fromStrict inText) >>=
(first toException . parser)
first :: (a -> c) -> Either a b -> Either c b
first f (Left l) = Left (f l)
first _ (Right r) = Right r
parser :: MonadThrow m => Element -> m TestElement
parser elmt =
do unless (elementName elmt == "TestElement")
$ throwM $ TagMismatch "TestElement"
return TestElement
unitTests :: TestTree
unitTests = testGroup "Unit tests"
[ {-...-}
testCase "parseTxt parser failTxt == fail" $
(first aux $ parseTxt parser failTxt) @?= Left $ TagMismatch "TestElement"
]
where aux = fromMaybe (error "converting from SomeException failed")
. fromException
Note1: the deriving Eq
is only necessary for the @?=
operation in the unit tests and could be omitted for the productive version of the code. 注意1:仅当单元测试中的
@?=
运算时才需要deriving Eq
,对于有效版本的代码,则可以省略。
Note2: Also the direct dependency on resourcet
can be replaced by exceptions
, which the former just reexports. 注意2:同样,对
resourcet
的直接依赖性也可以由exceptions
代替, exceptions
只是重新导出。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.