2015-07-29 16 views
18

W Testing Monadic Code with QuickCheck (Claessen, Hughes 2002), assert ma typ:Test.QuickCheck.Monadic: dlaczego jest assert stosowane do Bool, nie sprawdzalne a => a

assert :: (Monad m, Testable a) => a -> PropertyM m() 

Jednak Test.QuickCheck.Monadic, to ma typ:

assert :: (Monad m) => Bool -> PropertyM m() 

Dlaczego assert ma ten ostatni typ w bibliotece?

+1

Być może komentarze ze źródła będą pomocne: [(link)] (https://github.com/nick8325/quickcheck/blob/master/Test/QuickCheck/Monadic.hs#L118-126). Interesujące, że komentarz do 'assert' ma sig papieru, a także' stop' w zasadzie ma to samo znaczenie. – ErikR

Odpowiedz

4

Myślę, że to z powodu ograniczeń technicznych, ponieważ obecnie ocenić Testable z biblioteką Test.QuickCheck, trzeba użyć jednej z quickCheck* funkcji, które są bardzo IO -centric. Dzieje się tak, ponieważ właściwości testu QuickCheck Testable poprzez losowe generowanie możliwych danych wejściowych (domyślnie 100), próbując znaleźć counterexample, co świadczy o fałszywej właściwości. Jeśli takie dane wejściowe nie zostaną znalezione, zakłada się, że właściwość jest prawdziwa (chociaż niekoniecznie jest to prawda, może to być kontrprzykład, który nie był testowany). Aby móc generować losowe dane wejściowe w Haskell, trzymamy się monady IO.

Należy zauważyć, że chociaż assert został zdefiniowany w taki ogólny sposób, jest używany przez cały papier tylko z Bool. Tak więc autor biblioteki (ten sam w dokumencie) wolał poświęcić ogólny parametr Testable dla zwykłego Bool, aby nie wymuszać żadnej monady w tym momencie.

I widzimy, że oni nawet napisane oryginalny podpis jako komentarz w source code:

-- assert :: Testable prop => prop -> PropertyM m() 

Należy również zauważyć, że pomimo faktu, że stop funkcja ma podobny podpis:

stop :: (Testable prop, Monad m) => prop -> PropertyM m a 

Jest to , a nie takie samo, jak funkcja assert w dokumencie, ponieważ pierwsza z nich będzie zatrzymywać obliczenia w b W innych przypadkach warunkiem jest True lub False. Z drugiej strony, assert zatrzyma tylko wyliczenie jeśli warunek jest False:

⟦dochodzić Prawdziwa »P⟧ = ⟦p⟧

⟦dochodzić Fałsz» P⟧ = {return false}

Możemy jednak łatwo napisać IO wersję funkcji assert z papieru:

import Control.Monad 
import Control.Monad.Trans 
import Test.QuickCheck 
import Test.QuickCheck.Monadic 
import Test.QuickCheck.Property 
import Test.QuickCheck.Test 

assertIO :: Testable prop => prop -> PropertyM IO() 
assertIO p = do r <- liftIO $ quickCheckWithResult stdArgs{chatty = False} p 
       unless (isSuccess r) $ fail "Assertion failed" 

A teraz możemy wykonać test, aby zobaczyć różnice między assertIO i stop:

prop_assert :: Property 
prop_assert = monadicIO $ do assertIO succeeded 
          assertIO failed 

prop_stop :: Property 
prop_stop = monadicIO $ do stop succeeded 
          stop failed 

main :: IO() 
main = do putStrLn "prop_assert:" 
      quickCheck prop_assert 
      putStrLn "prop_stop:" 
      quickCheck prop_stop 

succeeded i failed można zastąpić True i False, odpowiednio. Chodziło o pokazanie, że teraz nie jesteśmy ograniczeni do Bool, zamiast tego możemy użyć dowolnego Testable.

a wyjście jest:

prop_assert:
*** nie powiodło się! Asercja nie powiodła się (po 1 teście):
prop_stop:
+++ OK, zaliczyła 100 testów.

Jak widać, mimo że pierwsza assertIO udało prop_assert powiodło się z powodu drugiego assertIO. Z drugiej strony test zakończył się pomyślnie, ponieważ pierwsze stop powiodło się i obliczenia zostały zatrzymane w tym momencie, nie testując drugiego stop.

+0

Ale z jakiego powodu funkcja 'quickCheck *' jest bardzo skoncentrowana na IO ', jeśli prowadzi do rezygnacji z pewnej ogólności w bibliotece, zgodnie z propozycją artykułu. – frasertweedale

+0

@frasertweedale Problem polega na tym, że testy QuickCheck umożliwiają testowanie właściwości poprzez losowe generowanie możliwych danych wejściowych (domyślnie 100), próbując znaleźć [countererexample] (https://en.wikipedia.org/wiki/Counterexample), który jest dowodem właściwości fałszywy. Jeśli takie dane wejściowe nie zostaną znalezione, właściwość zostanie przyjęta jako prawdziwa (choć niekoniecznie jest to prawda, może to być kontrprzykład, który nie był testowany). Aby móc generować losowe dane wejściowe w Haskell, utknęliśmy w monadzie "IO". Zauważ także, że nawet jeśli 'assert' został zdefiniowany w taki ogólny sposób, to jest używany przez cały papier tylko z' Bool'. –

+0

OK, zaczyna być nieco jaśniejszy. Powyższe punkty powinny być uwzględnione w Twojej odpowiedzi. – frasertweedale