2013-03-09 32 views
4

我正在測試一個隨機生成器來生成我自己類型的實例。對於我的Arbitrary一個自定義實例:Test.QuickCheck:加速測試同一類型的多個屬性

complexGenerator :: (RandomGen g) => g -> (MyType, g) 

instance Arbitrary MyType where 
    arbitrary = liftM (fst . complexGenerator . mkStdGen) arbitrary 

這與Test.QuickCheck效果很好(其實,Test.Framework)進行測試,生成的值保持一定的性能。但是,我想檢查的屬性有很多,而且我添加的內容越多,驗證所需的時間就越多。

有沒有辦法使用相同的生成值來測試每個屬性,而不是每次重新生成它們?我顯然還是希望看到,在失敗的情況下,其中屬性沒有佔用,因此使得and屬性不是最佳。

回答

3

我顯然還是想看到的,在失敗,其財產不追究,所以使一個巨大的財產與and不是最優的。

你可以做一個巨大的財產與conjoin之前標籤中使用printTestCase每個屬性。

例如你認爲這是一個壞主意:

prop_giant :: MyType -> Property 
prop_giant x = conjoin [printTestCase "one" $ prop_one x, 
         printTestCase "two" $ prop_two x, 
         printTestCase "three" $ prop_three x] 

(話雖如此,我從來沒有用過這種方法我自己和我:

prop_giant :: MyType -> Bool 
prop_giant x = and [prop_one x, prop_two x, prop_three x] 

這將是爲高效而給你更好的輸出只是假設它會起作用; conjoin可能在文檔中標記爲試驗性的原因。)

1

結合投票答案,我發現有用的是使用Writer monad的Reader變形器:

type Predicate r = ReaderT r (Writer String) Bool 

閱讀器「共享環境」是本例中的測試輸入。然後,你可以撰寫性質是這樣的:

inv_even :: Predicate Int 
inv_even = do 
    lift . tell $ "this is the even invariant" 
    (==) 0 . flip mod 2 <$> ask 

toLabeledProp :: r -> Predicate r -> Property 
toLabeledProp cause r = 
    let (effect, msg) = runWriter . (runReaderT r) $ cause in 
    printTestCase ("inv: " ++ msg) . property $ effect 

,並結合:

fromPredicates :: [Predicate r] -> r -> Property 
fromPredicates predicates cause = 
    conjoin . map (toLabeledProp cause) $ predicates 

我懷疑有涉及類似的東西要麼或WriterT這裏 - 這將簡潔組成不同類型的謂詞到另一種方法一個結果。但至少,這允許記錄依賴於輸入值而施加不同後置條件的屬性。

編輯:這個想法催生了庫: http://github.com/jfeltz/quickcheck-property-comb