簡體   English   中英

HTF不測試TH生成的道具

[英]HTF does not test props generated by TH

我想對我的庫中的各種類型進行許多類似的測試。

為簡化起見,假設我有許多實現Num類的向量類型,並且我想生成相同的prop_absNorm xy = abs x + abs y >= abs (x+y)屬性檢查prop_absNorm xy = 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)
        |])
 )

我的生成測試的功能具有以下簽名:

writeTests :: (TypeQ -> Q [Dec]) -> Q [Dec]

該函數通過VectorMath (n::Nat) t查找我的矢量類VectorMath (n::Nat) t所有實例(並同時reify ''VectorMath Num實例),並相應地生成所有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)

問題在於,將檢查所有手動編寫的屬性,但不會檢查生成的屬性。

注意1:我已經在同一文件中生成和未生成屬性(即TH表達式$(..)與其他道具在同一文件中)。

注意2:用於創建prop函數的類型列表是可變的-我想稍后再添加VectorMath其他實例,因此它們會自動添加到測試列表中。

我認為問題在於HTF(大概也使用TH)解析原始文件,而不是解析生成的文件-但是我不知道為什么會發生這種情況。

所以我的問題是:如何解決這個問題? 如果不可能使用TH生成的道具,那么是否可以對各種類型進行prop_absNorm :: Vector 4 a -> Vector 4 a -> Bool測試(即將其替換為prop_absNorm :: Vector 4 a -> Vector 4 a -> Bool )?

另外一種替代方法是進一步使用TH將測試條目手動添加到htf_Main中,但是我還沒有弄清楚如何執行此操作。 而且它看起來並不是一個很好的干凈解決方案。

如果您事先知道生成的屬性測試的名稱是什么,那么您始終可以手動定義存根,以便HTF可以看到它們,例如:

$(generate prop test for Int)
$(generate prop test for CInt)

prop_p1 = prop_absNormInt
prop_p2 = prop_absNormCInt

HTF將測試視為prop_p1prop_p2 您不必在這些存根上放置類型簽名。

另一個想法是創建自己的源預處理程序,以便為您添加這些存根(並給它們更好的名稱)。 您的源預處理器將自動調用htfpp來完成預處理。

如果您向我展示如何調用TH,那么我可以向您展示如何編寫預處理程序。

更新:

鑒於您的評論,我將考慮執行以下操作:

  1. 編寫程序以生成測試模塊源。
  2. 在陰謀項目中包括該程序及其生成的輸出。
  3. 告訴用戶如果要更新測試模塊,請運行該程序。

因此-測試用例保持固定,直到程序運行以重新生成測試模塊。

擁有靜態測試模塊的優勢在於,您可以准確地說出要測試的內容。

有了重新創建測試模塊的程序,您便可以在新的Num實例可用時輕松地對其進行更新。

好的,我設法解決了這個問題。 想法是使用TH匯總測試並將其插入htfMain 除了我要回答的問題之外,還包括以下步驟:

  1. 將所有可測試的屬性轉換為運行QuickCheck測試的IO操作;
  2. 將所有測試匯總到TestSuite
  3. 將所有測試套件匯總到一個列表中,然后放入htfMain

為了使用步驟1,我不得不使用稱為qcAssertion :: (QCAssertion t) => t -> Assertion的HTF的半內部函數。 此功能可用,但不建議在外部使用。 它可以很好地運行QuickCheck測試,並將其集成到報告中。

要繼續執行步驟2,我使用了HTF的兩個函數: makeTestSuitemakeQuickCheckTest 我還使用TH的location功能來提供文件名和插入帶有測試模板的接頭的位置的行(以獲取更好的測試日志)。

步驟3是一個棘手的步驟:為此,我們需要找到所有生成的測試套件。 問題是TH不允許瀏覽模塊中的所有功能(包括生成的功能)。 為了克服這個問題,我添加了以下類型類:

class MultitypeTestSuite name where
    multitypeTestSuite :: name -> TestSuite

所以,我的功能writeTests生成新的數據類型data MTS[prop_name]和實例MultitypeTestSuite該數據類型。 這使我在以后使用中htfMain另一個拼接功能,將生成的測試套件使用了該類的實例的列表reify

aggregateTests :: ExpQ
aggregateTests = do
    ClassI _ instances <- reify ''MultitypeTestSuite
    liftM ListE . forM instances
          $ \... -> [e| multitypeTestSuite $(...) |]

最后,包括所有生成的測試以及手動編寫的測試,看起來非常簡單:

main :: IO ()
main = htfMain $ htf_importedTests ++ $(aggregateTests)

因此,通過調整函數$(writeTests)我現在能夠生成和測試因參數類型而異的屬性-對於同一類型作用域中可用的所有類型。 測試結果和日志的記錄方式與原始測試相同。

至此,問題得到了徹底解決。

HTF不使用TemplateHaskell收集測試,這會大大降低編譯時間。 取而代之的是,HTF使用名為htfpp的自定義預處理器。 htfpp 編譯器之前運行(因此在TemplateHaskell接頭擴展之前運行)。 這意味着在使用TemplateHaskell生成測試時, htfpphtfpp使用自動測試發現。

我的建議:無論如何,當您使用TemplateHaskell時,只需使用TemplateHaskell來收集您生成的測試用例。 該功能未內置在HTF中,但實現此功能並不困難。 就這個:

-- file TH.hs
{-# LANGUAGE TemplateHaskell #-}
module TH ( genTestSuiteFromQcProps ) where

import Language.Haskell.TH

import Test.Framework
import Test.Framework.Location

genTestSuiteFromQcProps :: String -> [Name] -> Q Exp
genTestSuiteFromQcProps suiteName names =
    [| makeTestSuite $(stringE suiteName) $(listE genTests) |]
    where
      genTests :: [ExpQ]
      genTests =
          map genTest names
      genTest :: Name -> Q Exp
      genTest name =
          [| makeQuickCheckTest $(stringE (show name)) unknownLocation
                 (qcAssertion $(varE name)) |]

genTestSuiteFromQcProps函數采用要生成的測試套件的名稱和名稱列表,以引用您的QC屬性。 genTestSuiteFromQcProps返回類型為TestSuite的表達式。 TestSuite是HTF用於組織測試的類型之一。 htfpp預處理程序als在其輸出中使用TestSuite類型。)

這是您使用genTestSuiteFromQcProps

-- file Main.hs
{-# OPTIONS_GHC -F -pgmF htfpp #-}
{-# LANGUAGE TemplateHaskell #-}
module Main where

import TH
import Test.Framework

import {-@ HTF_TESTS @-} OtherTests

prop_additionCommutative :: Int -> Int -> Bool
prop_additionCommutative x y = (x + y) == (y + x)

prop_reverseReverseIdentity :: [Int] -> Bool
prop_reverseReverseIdentity l = l == reverse (reverse l)

myTestSuite :: TestSuite
myTestSuite =
    $(genTestSuiteFromQcProps
         "MyTestSuite"
         ['prop_additionCommutative
         ,'prop_reverseReverseIdentity])

main :: IO ()
main = htfMain (myTestSuite : htf_importedTests)

對於您的情況,您可以通過genTestSuiteFromQcProps傳遞使用TemplateHaskell生成的QC屬性的名稱。

該示例還顯示,您可以將使用TemplateHaskell函數生成的測試用例與htfpp收集的htfpp 為了完整OtherTests ,這是OtherTests的內容:

{-# OPTIONS_GHC -F -pgmF htfpp #-}
module OtherTests ( htf_thisModulesTests) where

import Test.Framework

test_someOtherTest :: IO ()
test_someOtherTest =
    assertEqual 1 1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM