2011-11-19 10 views
14

Cuando falla un valor de una prueba QuickCheck, me gustaría utilizarlo para la depuración. ¿Hay alguna manera de que pueda hacer algo como:Encuentre el valor que falló en la comprobación rápida

let failValue = quickCheck' myTest 
in someStuff failValue 

Si mis datos era capaz read entonces probablemente podría piratear alguna manera de conseguirlo desde IO, pero no lo es.

Respuesta

9

No pude encontrar nada en la API de QuickCheck para hacer esto de una manera agradable, pero he aquí algo que pirateé utilizando la API monádica QuickCheck. Se intercepta y registra las entradas a su propiedad en un IORef, y asume que si falló, el último fue el culpable y lo devuelve en un Just. Si la prueba pasó, el resultado es Nothing. Esto probablemente se pueda refinar un poco, pero para las propiedades simples de un argumento, debería hacer el trabajo.

import Control.Monad 
import Data.IORef 
import Test.QuickCheck 
import Test.QuickCheck.Monadic 

prop_failIfZero :: Int -> Bool 
prop_failIfZero n = n /= 0 

quickCheck' :: (Arbitrary a, Show a) => (a -> Bool) -> IO (Maybe a) 
quickCheck' prop = do input <- newIORef Nothing 
         result <- quickCheckWithResult args (logInput input prop) 
         case result of 
         Failure {} -> readIORef input 
         _ -> return Nothing 
    where 
    logInput input prop x = monadicIO $ do run $ writeIORef input (Just x) 
              assert (prop x) 
    args = stdArgs { chatty = False } 

main = do failed <- quickCheck' prop_failIfZero 
      case failed of 
       Just x -> putStrLn $ "The input that failed was: " ++ show x 
       Nothing -> putStrLn "The test passed" 
+0

muy inteligente, gracias – Xodarap

+0

Este pequeño truco acaba de hacer que mi experiencia en la depuración de Haskell sea mucho mejor. Gracias –

2

Una forma sería utilizar el método sample', ejecutar manualmente la prueba y encontrar los valores en los que falla. Por ejemplo, las pruebas de una doble función defectuosa:

import Test.QuickCheck 

double :: Int -> Int 
double x | x < 10 = 2 * x 
     | otherwise = 13 

doubleTest :: Int -> Bool 
doubleTest x = x + x == double x 

tester :: IO() 
tester = do 
    values <- sample' arbitrary 
    let failedValues = filter (not . doubleTest) values 
    print failedValues 

El único problema es sample' sólo genera 11 valores de prueba, que pueden no ser suficiente para desencadenar el error.

Cuestiones relacionadas