2013-03-09 4 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 -> Bool 
prop_giant x = and [prop_one x, prop_two x, prop_three x] 

это будет столь же эффективным еще дать вам лучший выход:

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

(Сказав это, я никогда не использовал этот метод сам, и я только при условии, что будет работать, conjoin, вероятно, помечена как экспериментальные в документации по причине)

1

в сочетании с голосовавшим ответом, что я нашел полезным является использование чтения трансформатора с Writer монадой:

.
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 здесь- который бы сжато сочинить предикаты на различные типы в один результат. Но, по крайней мере, это позволяет документировать свойства, которые накладывают разные постконвенции, зависящие от значения ввода.

Edit: Эта идея породила библиотеку: http://github.com/jfeltz/quickcheck-property-comb