Если набор тестов предназначен для QuickCheck, я советую использовать вместо него новый модуль All
:
http://hackage.haskell.org/packages/archive/QuickCheck/2.4.1.1/doc/html/Test-QuickCheck-All.html
Он делает то же самое, за исключением того, что выбирает имена свойств, обращаясь к файловой системе и анализируя файл, в котором находится соединение (если вы используете какой-то другой тестовый фреймворк, вы все равно можете использовать тот же подход). *
Если вы действительно хотите заключить в кавычки весь файл, вы можете вместо этого использовать квазиквотер (который не требует отступа). Вы можете легко построить свою цитату на 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