Haskell Tasty.HUnit:如何使用IO运行多个测试

3
我正在尝试在Test.Tasty testGroup中运行多个测试(即多个Assertions),但只能输入从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(毕竟Assertion只是IO());但这可能意味着将重复执行IO,这不是我想要的。
再次提供另一种选择,testCase可以包括一个调用多个assertions的do {}块;但这将意味着无法选择单个测试,并且不会得到详细输出以确认运行了哪些测试。 Test.Tasty.withResource看起来很有希望;如果它的第三个参数是a -> TestTree,那么我可以使用它;但是,它不是,而是IO a -> TestTree,我正在努力弄清楚如何安全地提取a以在我的测试用例中使用。
我已经尝试过一些方法,但我担心我可能错过了一些基本的东西...
非常感谢任何帮助。
1个回答

2
最初的回答
您说得对,关注以下内容:
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测试,则不运行)。

真让我震惊,你是对的!非常感谢@phadej。我看到了这条评论,但第一遍看时它引起了我的认知失调,我不知怎么就忘了它。 - user3416536
这个例子对我来说无法通过类型检查。withResource 的第二个参数即 (a -> IO ()) 未给定。如果您能添加它,那就太好了,可以改善答案。 - twitu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接