art*_*tem 5 haskell quickcheck template-haskell htf
我想在我的库中对各种类型进行一些类似的测试.
为简化起见,假设我有许多实现Num类的向量类型,并且我想生成相同的QuickCheck属性检查prop_absNorm x y = abs x + abs y >= abs (x+y),该检查可以对库中的所有类型起作用.
我使用TH生成这样的属性:
$(writeTests
(\t ->
[d| prop_absNorm :: $(t) -> $(t) -> Bool
prop_absNorm x y = abs x + abs y >= abs (x+y)
|])
)
Run Code Online (Sandbox Code Playgroud)
我生成测试的函数具有以下签名:
writeTests :: (TypeQ -> Q [Dec]) -> Q [Dec]
Run Code Online (Sandbox Code Playgroud)
此函数查找我的矢量类的所有实例VectorMath (n::Nat) t(以及同时的实例Num)reify ''VectorMath并相应地生成所有prop函数.
-ddump-splices显示这样的事情:
prop_absNormIntX4 :: Vector 4 Int -> Vector 4 Int -> Bool
prop_absNormIntX4 x y = abs x + abs y >= abs (x+y)
prop_absNormCIntX4 :: Vector 4 CInt -> Vector 4 CInt -> Bool
prop_absNormCIntX4 x y = abs x + abs y >= abs (x+y)
...
prop_absNormFloatX4 :: Vector 4 Float -> Vector 4 Float -> Bool
prop_absNormFloatX4 x y = abs x + abs y >= abs (x+y)
prop_absNormFloatX3 :: Vector 3 Float -> Vector 3 Float -> Bool
prop_absNormFloatX3 x y = abs x + abs y >= abs (x+y)
Run Code Online (Sandbox Code Playgroud)
问题是所有手动编写的属性都被检查,但生成的属性不是.
注1:我在同一个文件中生成了非生成属性(即TH表达式$(..)与其他道具在同一个文件中).
注2:用于创建prop函数的类型列表是可变的 - 我想VectorMath稍后添加其他实例,因此它们会自动添加到测试列表中.
我相信问题是HTF(可能也使用TH)解析原始文件,而不是生成代码的文件 - 但我不知道为什么会发生这种情况.
所以我的问题是:如何解决这个问题?如果不可能使用TH生成的道具,那么可以对各种类型进行QuickCheck测试(即它可以替代它们prop_absNorm :: Vector 4 a -> Vector 4 a -> Bool)吗?
另一个替代方案可能是使用TH进一步手动将测试条目添加到htf_Main,但我还没想出如何做到这一点; 它看起来不是一个很好的清洁解决方案.
好的,我设法解决了这个问题。这个想法是使用 TH 来聚合测试并将它们插入到htfMain. 除了我在问题中提到的内容之外,还包括以下步骤:
IO运行 QuickCheck 测试的操作;TestSuite;htfMain.为了使用步骤 1,我必须使用 HTF 的半内部函数qcAssertion :: (QCAssertion t) => t -> Assertion. 该功能可用,但不建议外部使用;它允许很好地运行 QuickCheck 测试,并将它们集成到报告中。
为了继续步骤 2,我使用 HTF 中的两个函数:makeTestSuite和makeQuickCheckTest。我还使用locationTH 的函数来提供文件名和插入测试模板的拼接位置的行(以获得更好的测试日志)。
第 3 步是一个棘手的步骤:为此我们需要找到所有生成的测试套件。问题是 TH 不允许浏览模块中的所有函数(包括生成的函数)。为了克服这个问题,我添加了以下类型类:
class MultitypeTestSuite name where
multitypeTestSuite :: name -> TestSuite
Run Code Online (Sandbox Code Playgroud)
所以我的函数writeTests生成一个新的数据类型和该数据类型data MTS[prop_name]的实例。MultitypeTestSuite这允许我稍后在 htfMain 中使用另一个 splice 函数,该函数将从该类的实例中生成测试套件列表,使用reify:
aggregateTests :: ExpQ
aggregateTests = do
ClassI _ instances <- reify ''MultitypeTestSuite
liftM ListE . forM instances
$ \... -> [e| multitypeTestSuite $(...) |]
Run Code Online (Sandbox Code Playgroud)
最后,将所有生成的测试与手动编写的测试一起包含起来看起来非常简单:
main :: IO ()
main = htfMain $ htf_importedTests ++ $(aggregateTests)
Run Code Online (Sandbox Code Playgroud)
因此,通过调整函数,$(writeTests)我现在可以生成和测试参数类型不同的属性 - 对于同一类型范围内可用的所有类型。测试结果和日志的包含方式与原始测试相同。
至此问题就完全解决了。
| 归档时间: |
|
| 查看次数: |
135 次 |
| 最近记录: |