Come visualizzare un motivo di una proprietà test fallito con QuickCheck?
-
23-10-2019 - |
Domanda
Qual è la migliore pratica per motivi di visualizzazione per un test di proprietà fallito quando viene testato tramite QuickCheck?
Si consideri ad esempio:
prop a b = res /= []
where
(res, reason) = checkCode a b
Poi l'una sessione potrebbe essere simile:
> quickCheck prop
Falsifiable, after 48 tests:
42
23
Ma per il debug sarebbe davvero comodo per mostrare il motivo per il fallimento come parte della relazione falsifable QuickCheck.
ho violato in questo modo:
prop a b = if res /= [] then traceShow reason False else True
where
(res, reason) = checkCode a b
è che c'è un modo migliore / più o più quickcheckish per farlo?
Soluzione
Presumo che la variabile "ragione" contiene un qualche tipo di dati di test specifici su cosa è andato storto. Si potrebbe invece restituire un "risultato", che contiene sia il successo / fallimento / condizioni non valide e una stringa che spiega cosa è andato storto. Proprietà che i risultati di ritorno sono gestite da QuickCheck esattamente nello stesso modo in cui le proprietà che restituiscono Bool.
(Modifica) Ti piace questa:
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 [] []
Si noti che è il "risultato" tipo definito nel Test.QuickCheck.Property si desidera.
Ci sono anche alcuni combinatori definiti in Test.QuickCheck.Property che consente di inquadrare meglio il risultato piuttosto che chiamare direttamente la funzione di costruzione, come ad esempio
prop_three :: Integer -> Property
prop_three n = printTestCase ("always fails: n = " ++ show n) False
Credo che sarebbe stato lo stile meglio utilizzare quelli.
Altri suggerimenti
A causa QuickCheck ti dà gli input alla funzione, e perché il codice in prova è pura (che è, giusto?), Si può solo alimentare gli ingressi alla funzione e ottenere il risultato. Questo è più flessibile, perché con gli ingressi si possono anche ripetutamente test con modifiche alla funzione originaria fino a quando non è corretto.
Questa è la mia soluzione (io uso counterexample
invece di printTestCase
dopo quella successiva è deprecata ora):
(<?>) :: (Testable p) => p -> String -> Property
(<?>) = flip (Test.QuickCheck.counterexample . ("Extra Info: " ++))
infixl 2 <?>
Utilizzo:
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)
Così, quando un banco di prova riesce, è possibile vedere qualcosa di simile:
*** Failed! Falsifiable, Falsifiable (after 2 tests):
1
-1
Extra Info: (1,1,0)
È anche possibile utilizzare <?>
insieme .&&.
, .||.
, ===
e ==>
ecc.
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"
Questo funziona nello stesso modo come la risposta di Paul Johnson, ma è più conciso e robusto alle variazioni di 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