このケースをテストするためのQuickCheckテストケースを作成するにはどうすればよいですか?
あなたはすべきではありません!QuickCheckは、プロパティベースのテスト用のツールです。プロパティベースのテストでは、データ構造(またはその他)のプロパティを指定すると、テストツールが自動的にテストケースを生成して、そのプロパティが生成されたテストケースに当てはまるかどうかを確認します。では、のような具体的なテストケースを与える代わりに、どのようにプロパティを与えることができるのか、[1,2,3]
そしてなぜプロパティが有利なのかを見てみましょう!
それで。私はから始めました
import Test.QuickCheck
import qualified Data.Set as Set
import Data.Set (Set)
data Profile = Profile (Set Int)
deriving (Eq, Show)
mkProfile :: [Int] -> Profile
mkProfile = Profile . Set.fromList
-- | We will test if the order of the arguments matter.
test_mkProfile :: [Int] -> Bool
test_mkProfile xs = (mkProfile xs `comp` mkProfile (reverse xs))
where comp | length xs <= 1 = (==)
| otherwise = (/=)
これが私が自分のプロパティを推論した方法です。空のシングルトンリストの場合、それは単なるIDなので、と同じであるとreverse
期待します。右?つまり、まったく同じ引数を取得します。その場合は明らかにそうではありません。のように。そして、プロファイルが順序を気にすることを私たちは知っています。mkProfile xs
mkProfile (reverse xs)
mkProfile
length xs >= 2
reverse xs
xs
reverse [1, 2] /= [2, 1]
では、これを試してみましょうghci
*Main> quickCheck test_mkProfile
*** Failed! Falsifiable (after 3 tests and 1 shrink):
[0,0]
ここで、コードには実際には2つの間違いがあることに注意してください。1つは、まず、Profile
セットではなくリストを使用する必要があります。第二に、私たちの財産は間違っています!たとえlength xs >= 2
、xs == reverse (xs)
が真実である可能性があるからです。最初のエラーを修正して、クイックチェックで2番目の欠陥がどのように指摘されるかを見てみましょう。
data Profile2 = Profile2 [Int]
deriving (Eq, Show)
mkProfile2 :: [Int] -> Profile2
mkProfile2 = Profile2
-- | We will test if the order of the arguments matter.
test_mkProfile2 :: [Int] -> Bool
test_mkProfile2 xs = (mkProfile2 xs `comp` mkProfile2 (reverse xs))
where comp | length xs <= 1 = (==)
| otherwise = (/=)
コードは正しいのですが、プロパティに欠陥があることを忘れないでください。
*Main> quickCheck test_mkProfile2
+++ OK, passed 100 tests.
*Main> quickCheck test_mkProfile2
+++ OK, passed 100 tests.
*Main> quickCheck test_mkProfile2
+++ OK, passed 100 tests.
*Main> quickCheck test_mkProfile2
+++ OK, passed 100 tests.
*Main> quickCheck test_mkProfile2
+++ OK, passed 100 tests.
*Main> quickCheck test_mkProfile2
+++ OK, passed 100 tests.
*Main> quickCheck test_mkProfile2
+++ OK, passed 100 tests.
*Main> quickCheck test_mkProfile2
*** Failed! Falsifiable (after 8 tests):
[-8,-8]
はい。あなたはまだ考える必要があります!または、コードが文字通り700のテストケースに合格したという理由だけで、すべてが問題ないという誤った印象を受ける可能性があります。では、プロパティも修正しましょう。
test_mkProfile2_again :: [Int] -> Bool
test_mkProfile2_again xs = (mkProfile2 xs `comp` mkProfile2 ys)
where ys = reverse xs
comp | xs == ys = (==)
| otherwise = (/=)
それが複数回機能することを見てみましょう!
*Main> import Control.Monad
*Main Control.Monad> forever $ quickCheck test_mkProfile2_again
+++ OK, passed 100 tests.
+++ OK, passed 100 tests.
+++ OK, passed 100 tests.
+++ OK, passed 100 tests.
+++ OK, passed 100 tests.
+++ OK, passed 100 tests.
+++ OK, passed 100 tests.
... (a lot of times)
やったー。これで、実装のバグが解消されただけでなくProfile
、コードとそれに準拠するプロパティについての理解も深まりました。