Как отобразить причину неудачного тестового свойства с помощью QuickCheck?
-
23-10-2019 - |
Вопрос
Какова наилучшая практика для отображения причин неудачного теста свойства, когда он тестируется через QuickCheck?
Рассмотрим, например:
prop a b = res /= []
where
(res, reason) = checkCode a b
Тогда сеанс может выглядеть как:
> quickCheck prop
Falsifiable, after 48 tests:
42
23
Но для отладки было бы очень удобно показать причину неудачи в рамках отчета о быстрого разжигания QuickCheck.
Я взломал это так:
prop a b = if res /= [] then traceShow reason False else True
where
(res, reason) = checkCode a b
Есть ли лучший/приятный или более быстрый способ сделать это?
Решение
Я предполагаю, что ваша переменная «разум» содержит какие-то конкретные данные о том, что пошло не так. Вместо этого вы можете вернуть «результат», который содержит как успех/неверный/недопустимые условия, и строка, объясняющая, что пошло не так. Свойства, которые возвращают результаты, обрабатываются QuickCheck точно так же, как и свойства, которые возвращают Bool.
(Редактировать)
module QtTest where
import Test.QuickCheck
import Test.QuickCheck.Property as P
-- Always return success
prop_one :: Integer -> P.Result
prop_one _ = MkResult (Just True) True "always succeeds" False [] []
-- Always return failure
prop_two :: Integer -> P.Result
prop_two n = MkResult (Just False) True ("always fails: n = " ++ show n) False [] []
Обратите внимание, что это тип «результата», определенный в Test.quickcheck.property, который вы хотите.
Есть также некоторые комбинаторы, определенные в тесте.
prop_three :: Integer -> Property
prop_three n = printTestCase ("always fails: n = " ++ show n) False
Я думаю, было бы лучшим стилем использовать их.
Другие советы
Поскольку QuickCheck дает вам входные данные для функции, и поскольку тестовый код в тестировании чисто (он есть, верно?), Вы можете просто подарить эти входы в функцию и получить результат. Это более гибко, потому что с этими входами вы также можете неоднократно проверять с настройками исходной функции, пока она не станет правильной.
Это мое решение (я использую counterexample
вместо printTestCase
С тех пор, как теперь устарел):
(<?>) :: (Testable p) => p -> String -> Property
(<?>) = flip (Test.QuickCheck.counterexample . ("Extra Info: " ++))
infixl 2 <?>
Применение:
main :: IO ()
main = hspec $ do
describe "math" $ do
prop "sum-of-square-le-square-of-sum" $ do
\(x :: Int) (y :: Int) ->
x * x + y * y <= (x + y) * (x + y) <?> show (x * x, y * y, x + y)
Поэтому, когда тестовый пример не удается, вы можете увидеть что -то вроде:
*** Failed! Falsifiable, Falsifiable (after 2 tests):
1
-1
Extra Info: (1,1,0)
Вы также можете использовать <?>
вместе с .&&.
, .||.
, ===
а также ==>
так далее.:
describe "math" $ do
prop "sum-of-square-le-square-of-sum" $ do
\(x :: Int) (y :: Int) ->
x * x + y * y <= (x + y) * (x + y) <?> show (x * x, y * y, x + y) .||. (1==0) <?> "haha"
Это работает так же, как и ответ Пола Джонсона, но более кратко и устойчиво к изменениям в MkResult
:
import Test.QuickCheck.Property (succeeded, failed, reason)
prop a b =
if res /= []
then succeeded
else failed { reason = reason }
where
(res, reason) = checkCode a b