我正在尝试在Test.Tasty testGroup中运行多个测试(即多个断言);但要输入从IO中读取的单个“对象”。
例如,我读取并解析文件;我想对该文件的结果进行多个断言。像
tests :: [String] -> TestTree
tests ls = testGroup "tests" [ testCase "length" $ length ls @?= 2
, testCase "foo" $ ls !! 0 @?= "foo"
]
main = do
ls :: [String] <- read <$> readFile "/tmp/hunit"
defaultMain (tests ls)
但是,以上要求在调用测试之前执行IO;即使仅请求测试的一个子集(无论该子集是否实际使用IO结果)也可以执行该操作。
或者,每个testCase都可以执行自己的IO(毕竟,断言只是IO());但这可能意味着要重复执行IO,这不是我想要的。
或者,testCase可以包含一个do {}
块,该块调用多个断言。但这将意味着单个测试是不可选择的,并且不会得到详细的输出来确认运行了哪些测试。
Test.Tasty.withResource
看起来充满希望;如果它的第三个参数是a -> TestTree
,我可以解决这个问题;但是不是,它是IO a -> TestTree
,我正在努力研究如何安全地提取a
以便在测试用例中使用。
我尝试过使用它,但是我担心我缺少一些基本的东西...
非常感谢您的帮助。
答案 0 :(得分:1)
似乎应该很简单;自type Assertion = IO ()
起,这两部分就足够了:
(>>=) :: IO a -> (a -> Assertion) -> Assertion
testCase :: TestName -> Assertion -> TestTree
答案 1 :(得分:0)
您正在看
withResource
:: IO a -- ^ initialize the resource
-> (a -> IO ()) -- ^ free the resource
-> (IO a -> TestTree)
-- ^ @'IO' a@ is an action which returns the acquired resource.
-- Despite it being an 'IO' action, the resource it returns will be
-- acquired only once and shared across all the tests in the tree.
-> TestTree
想法是您可以将方案编写为:
tests :: IO String -> TestTree
tests lsIO = testGroup "tests"
[ testCase "length" $ do
ls <- lsIO
length ls @?= 2
, testCase "foo" $ do
ls <- lsIO
ls !! 0 @?= "foo"
, testCase "no io" $ do
return ()
]
main :: IO ()
main = defaultMain (withResource acquire tests)
acquire :: IO [String]
acquire = read <$> readFile "/tmp/hunit"
即好像您多次读取文件,但是tasty
仅执行一次该操作。那就是评论所说的:)
尝试将putStrLn "trace debug"
添加到acquire
以确保它几乎只运行一次(即,如果仅要求进行no io
测试则不运行)。