Pregunta

¿Cuál es la mejor práctica para mostrar razones para una prueba de propiedad fallida cuando se prueba a través de QuickCheck?

Considere por ejemplo:

prop a b = res /= []
   where
      (res, reason) = checkCode a b

Entonces la sesión A podría parecer:

> quickCheck prop
Falsifiable, after 48 tests:
42
23

Pero para la depuración sería realmente conveniente mostrar la razón del fracaso como parte del informe falsificable de QuickCheck.

Lo he pirateado así:

prop a b = if res /= [] then traceShow reason False else True
   where
      (res, reason) = checkCode a b

¿Hay una forma mejor/más agradable o más rápida de hacerlo de hacerlo?

¿Fue útil?

Solución

Supongo que su variable de "razón" contiene algún tipo de datos específicos de prueba sobre lo que salió mal. En su lugar, podría devolver un "resultado", que contiene condiciones de éxito/fallas/inválidas y una cadena que explica lo que salió mal. Las propiedades que devuelven los resultados son manejadas por QuickCheck exactamente de la misma manera que las propiedades que devuelven bool.

(editar) así:

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 [] []

Tenga en cuenta que es el tipo de "resultado" definido en test.quickcheck.property que desea.

También hay algunos combinadores definidos en test.quickcheck.property que lo ayudan a componer el resultado en lugar de llamar directamente al constructor, como

prop_three :: Integer -> Property
prop_three n = printTestCase ("always fails: n = " ++ show n) False

Supongo que sería un mejor estilo usarlos.

Otros consejos

Debido a que QuickCheck le brinda las entradas a la función, y debido a que el código en la prueba es puro (¿es, ¿verdad?), Puede alimentar esas entradas a la función y obtener el resultado. Esto es más flexible, porque con esas entradas también puede probar repetidamente con los ajustes a la función original hasta que sea correcta.

Esta es mi solución (uso counterexample en vez de printTestCase Dado que el posterior está en desuso ahora):

(<?>) :: (Testable p) => p -> String -> Property
(<?>) = flip (Test.QuickCheck.counterexample . ("Extra Info: " ++))
infixl 2 <?>

Uso:

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)

Entonces, cuando falla un caso de prueba, puede ver algo como:

   *** Failed! Falsifiable, Falsifiable (after 2 tests):
   1
   -1
   Extra Info: (1,1,0)

También puedes usar <?> Juntos con .&&., .||., === y ==> etc.:

  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"

Esto funciona de la misma manera que la respuesta de Paul Johnson, pero es más conciso y robusto para los cambios en 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top