2013-09-01 11 views

risposta

4

Prima una dichiarazione di non responsabilità: non sono un esperto di QuickCheck e non avevo esperienza con il controllo monadico prima della domanda, ma vedo lo stackoverflow come un'opportunità per imparare cose nuove. Se c'è una risposta da esperti che dice che questo può essere fatto meglio, rimuoverò il mio.

Supponiamo di avere una funzione test che può generare eccezioni utilizzando exitWith. Ecco come penso che puoi provarlo. La funzione chiave è protect, che rileva l'eccezione e la converte in qualcosa su cui è possibile testare.

import System.Exit 
import Test.QuickCheck 
import Test.QuickCheck.Property 
import Test.QuickCheck.Monadic 

test :: Int -> IO Int 
test n | n > 100 = do exitWith $ ExitFailure 1 
     | otherwise = do print n 
         return n 

purifyException :: (a -> IO b) -> a -> IO (Maybe b) 
purifyException f x = protect (const Nothing) $ return . Just =<< f x 

testProp :: Property 
testProp = monadicIO $ do 
    input <- pick arbitrary 
    result <- run $ purifyException test $ input 
    assert $ if input <= 100 then result == Just input 
          else result == Nothing 

Ci sono due svantaggi in questo, per quanto posso vedere, ma non ho trovato alcun modo su di loro.

  1. ho trovato alcun modo per estrarre l'eccezione ExitCode dal AnException che protect in grado di gestire. Pertanto, tutti i codici di uscita sono trattati nello stesso modo (sono mappati su Nothing). Mi sarebbe piaciuto avere:

    purifyException :: (a -> IO b) -> a -> IO (Either a ExitCode) 
    
  2. ho trovato alcun modo per testare il comportamento di I/O di test. Supponiamo test era:

    test :: IO() 
    test = do 
        n <- readLn 
        if n > 100 then exitWith $ ExitFailure 1 
          else print n 
    

    Allora come è possibile provarlo?

Apprezzerei anche le risposte più esperte.

2

La funzione QuickCheck expectFailure può essere utilizzata per gestire questo tipo di cose. Prendete questo semplice (e non raccomandato) framework di gestione degli errori:

import System.Exit 
import Test.QuickCheck 
import Test.QuickCheck.Monadic 

handle :: Either a b -> IO b 
handle (Left _) = putStrLn "exception!" >> exitWith (ExitFailure 1) 
handle (Right x) = return x 

e montare un paio di funzioni fittizie:

positive :: Int -> Either String Int 
positive x | x > 0  = Right x 
      | otherwise = Left "not positive" 

negative :: Int -> Either String Int 
negative x | x < 0  = Right x 
      | otherwise = Left "not negative" 

Ora possiamo testare alcune proprietà della gestione degli errori. In primo luogo, Right valori non dovrebbero risultare in eccezioni:

prop_returnsHandledProperly (Positive x) = monadicIO $ do 
    noErr <- run $ handle (positive x) 
    assert $ noErr == x 

-- Main*> quickCheck prop_returnsHandledProperly 
-- +++ OK, passed 100 tests. 

Leftsdovrebbe risultato eccezioni. Notare

+0

Giusto, ma questo sembra aspettarsi tutti i tipi di guasti. Possiamo isolare quelli generati dalle chiamate 'exitWith'? Possiamo affermare quale sarà il codice di errore? – nickie

+0

@nickie È possibile aggiungere un po 'di tubature aggiuntive all'errore che si gestisce se si desidera testare le proprietà sui codici di uscita. Cioè avere una funzione 'verifyCode :: O a b -> ExitCode' che' exitWith' chiama, e fare asserzioni sul comportamento di quello. Non so se è possibile andare oltre. – jtobin

+0

'expectFailure' sembra passare se un test fallisce, al contrario se tutti i test falliscono, che è più quello che sto cercando. – Dan

Problemi correlati