クイックチェックで失敗したテストプロパティの理由を表示するにはどうすればよいですか?
-
23-10-2019 - |
質問
QuickCheck 経由でプロパティ テストをテストするときに、失敗したプロパティ テストの理由を表示するベスト プラクティスは何ですか?
たとえば、次のことを考えてみましょう。
prop a b = res /= []
where
(res, reason) = checkCode a b
その場合、セッションは次のようになります。
> quickCheck prop
Falsifiable, after 48 tests:
42
23
しかし、デバッグの場合、quickCheck の改ざん可能レポートの一部として失敗の理由を表示すると非常に便利です。
私は次のようにハッキングしました:
prop a b = if res /= [] then traceShow reason False else True
where
(res, reason) = checkCode a b
それを行うためのより良い/優れた、またはよりクイックチェック的な方法はありますか?
解決
「reason」変数には、何がうまくいかなかったのかについての、ある種のテスト固有のデータが含まれていると思います。代わりに、成功/失敗/無効の両方の条件と、何が問題だったかを説明する文字列を含む「結果」を返すこともできます。Results を返すプロパティは、Bool を返すプロパティとまったく同じ方法で QuickCheck によって処理されます。
(編集) このように:
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 [] []
必要なのは Test.QuickCheck.Property で定義された「Result」タイプであることに注意してください。
Test.QuickCheck.Property には、コンストラクターを直接呼び出すのではなく、結果を作成するのに役立ついくつかのコンビネーターが定義されています。
prop_three :: Integer -> Property
prop_three n = printTestCase ("always fails: n = " ++ show n) False
それらを使用する方がより良いスタイルになると思います。
他のヒント
QuickCheckは関数への入力を提供し、テスト中のコードが純粋であるため(そうですか?)、これらの入力を関数にフィードして結果を取得できます。これらの入力を使用すると、元の関数が正しいまで微調整して繰り返しテストすることができるため、これはより柔軟です。
これが私のソリューションです(私は使用しています counterexample
それ以外の printTestCase
後では今では非難されているので):
(<?>) :: (Testable p) => p -> String -> Property
(<?>) = flip (Test.QuickCheck.counterexample . ("Extra Info: " ++))
infixl 2 <?>
使用法:
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)
したがって、テストケースが失敗した場合、次のようなものを見ることができます。
*** Failed! Falsifiable, Falsifiable (after 2 tests):
1
-1
Extra Info: (1,1,0)
使用することもできます <?>
一緒に .&&.
, .||.
, ===
と ==>
等。:
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"
これはポール・ジョンソンの答えと同じように機能しますが、より簡潔で堅牢です 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