如果测试套件用于 QuickCheck,我建议您改用新All
模块:http:
//hackage.haskell.org/packages/archive/QuickCheck/2.4.1.1/doc/html/Test-QuickCheck-All.html
它做同样的事情,只是它通过访问文件系统并解析拼接所在的文件来获取属性的名称(如果您正在使用其他一些测试框架,您仍然可以使用相同的方法)。
如果你真的想引用整个文件,你可以使用一个 quasi-quoter 代替(它不需要缩进)。您可以轻松地在 haskell-src-meta 上构建您的报价器,但我建议不要使用这种方法,因为它不支持某些 Haskell 功能,并且可能会给出糟糕的错误消息。
聚合测试套件是一个难题,可以扩展名称收集例程以某种方式遵循导入,但这是很多工作。这是一个解决方法:
您可以使用以下修改版本forAllProperties
:
import Test.QuickCheck
import Test.QuickCheck.All
import Language.Haskell.TH
import Data.Char
import Data.List
import Control.Monad
allProperties :: Q Exp -- :: [(String,Property)]
allProperties = do
Loc { loc_filename = filename } <- location
when (filename == "<interactive>") $ error "don't run this interactively"
ls <- runIO (fmap lines (readFile filename))
let prefixes = map (takeWhile (\c -> isAlphaNum c || c == '_') . dropWhile (\c -> isSpace c || c == '>')) ls
idents = nubBy (\x y -> snd x == snd y) (filter (("prop_" `isPrefixOf`) . snd) (zip [1..] prefixes))
quickCheckOne :: (Int, String) -> Q [Exp]
quickCheckOne (l, x) = do
exists <- return False `recover` (reify (mkName x) >> return True)
if exists then sequence [ [| ($(stringE $ x ++ " on " ++ filename ++ ":" ++ show l),
property $(mono (mkName x))) |] ]
else return []
[|$(fmap (ListE . concat) (mapM quickCheckOne idents)) |]
您还需要runQuickCheckAll
未从 All 导出的函数:
runQuickCheckAll :: [(String, Property)] -> (Property -> IO Result) -> IO Bool
runQuickCheckAll ps qc =
fmap and . forM ps $ \(xs, p) -> do
putStrLn $ "=== " ++ xs ++ " ==="
r <- qc p
return $ case r of
Success { } -> True
Failure { } -> False
NoExpectedFailure { } -> False
在您现在定义的每个测试模块中
propsN = $allProperties
其中N
是某个数字或其他唯一标识符(或者您可以在下面的步骤中使用相同的名称并使用限定名称)。
在您定义的主测试套件中
props :: [(String,Property)]
props = concat [props1, props2 ... propsN]
如果你真的想避免为每个模块添加一个列表成员,你可以制作一个 TH 脚本来生成这个列表。
要运行所有测试,您只需说
runTests = runQuickCheckAll quickCheckResult props