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

时间:2019-03-14 14:39:08

标签: haskell

我正在尝试在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以便在测试用例中使用。

我尝试过使用它,但是我担心我缺少一些基本的东西...

非常感谢您的帮助。

2 个答案:

答案 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测试则不运行)。