2012-04-06 7 views
15

私はちょうどHaskellコードの束でQuickCheckを使い始めました。私は時代の後ろにいる、私は知っている。この質問は二部構成です:Haskell QuickCheckのベストプラクティス(特に型クラスをテストする場合)

まず、クイックチェックの一般的なベストプラクティスは何ですか?これまでのところ、私は次のことを拾ってきました:

  • 名(他のすべてはキャメルケースであるので、迷惑な)あなたのテストは* prop_
  • あなたはおそらくそれをやっている内部をテストしているかどうかをテストし(コードをエクスポート間違った)
  • テストのプロパティではなく、例が
  • 012) if x is out of range, normalize x ≠ x(またはいくつかの他のこのような性質を言う、その代わり X is out of range, Y is in range
  • を言ってはいけません

しかし、私はまだ他のベストプラクティスについて把握しています。特に:

  • プロパティは保存されていますか?
    • 同じファイルですか?
    • test/ディレクトリにありますか? (その場合は、src/の商品をどのようにインポートしますか)
    • Properties/ディレクトリsrcの下に商品をどのようにインポートしますか?

最も重要なのは、どのように我々は、型クラスのプロパティをテストして行く傾向にあるのですか?私はプロパティ∀ x: prev (next x) == xをテストしたい

class Gen a where 
    next :: a -> a 
    prev :: a -> a 

:たとえば、以下の(簡体字)型クラスを考えます。もちろん、これには各インスタンスのテストを書くことが含まれます。特にテストがより複雑な場合は、各インスタンスに同じプロパティを記述するのは面倒です。このようなテストを一般化するための標準的な方法は何ですか?

+4

パラレル 'のsrc /'と 'テスト/'ディレクトリを使用するには、 'HS-ソース-dirsにを設定することをお勧めします:SRC、あなたの' .cabal'ファイルでtest'ているので、両方のディレクトリモジュール検索パスにあります。 – hammar

+2

内部にプロパティが存在しないのはなぜですか? – alternative

+0

彼らは確かにテストを受けるのが難しく、(私の経験では)実装の詳細の代わりにエクスポートされた動作をテストするほうがずっと便利です。 – So8res

答えて

10

prop_というコンベンションには、prop_で始まるすべての機能をテストとして実行するスクリプトが付属しています。だから実際の理由はありませんが、が目に見えます(関数fooのプロパティはprop_fooです)。

内部のテストに問題はありません。これを行う方法は2通りあります。

  • プロパティを内部モジュールと同じモジュールに配置します。これにより、モジュールが大きくなり、(CPPハッカーを使用しない限り)プロジェクトのQCに無条件で依存する必要があります。

  • エクスポートされていないモジュール内に、実際にエクスポートされる機能が別のモジュールから再エクスポートされた状態で内部構造を持っています。その後、.cabalファイルで指定されたフラグが使用されている場合にのみ、内部モジュールをQCプロパティを定義するものにインポートし、そのモジュールはビルドされ、QC依存性を持ちます。

プロジェクトは、大規模である(区別を有するテスト内部からあなたを防ぐかもしれませんが)、その後、別src/test/ディレクトリが有用である可能性がある場合。しかし、あなたのプロジェクトがそれほど大きなものではない場合(とにかくモジュール全体の階層全体に存在する)、そのように分割する必要はありません。

Norman Ramseyが答えたところでは、タイプクラスの場合は、にプロパティを定義して、の型定義とそれに応じて使用することができます。

+0

私たちがしばしばやることは、(同じ.cabalファイルに定義されたライブラリではなく)内部モジュールに直接依存するCabalの 'test-suite'セクションを作成し、それを追加コンパイルを犠牲にしてテストできるようにすることです時間。 – tibbe

15

それはあなたがこれをしない各インスタンス

に同じプロパティを記述するために退屈です。あなたは、クラスのために一度プロパティを記述します。

class Gen a where 
    next :: a -> a 
    prev :: a -> a 

np_prop :: (Eq a, Gen a) => a -> Bool 
np_prop a = prev (next a) == a 

そして、それをテストするために、あなたが特定の型にキャスト:

quickCheck (np_prop :: Int -> Bool) 
quickCheck (np_prop :: String -> Bool) 

あなたの他の質問は、私は助けることができません。

3

型クラスはArbitraryインスタンスが定義されている存在量化辞書に具体化され

{-# LANGUAGE GADTs, ScopedTypeVariables #-} 
import Test.QuickCheck hiding (Gen) 

class Gen a where 
    next :: a -> a 
    prev :: a -> a 

np_prop :: SomeGen -> Bool 
np_prop (SomeGen a) = prev (next a) == a 

main :: IO() 
main = quickCheck np_prop 

instance Gen Bool where 
    next True = False 
    next False = True 
    prev True = False 
    prev False = True 

instance Gen Int where 
    next = (+ 1) 
    prev = subtract 1 

data SomeGen where 
    SomeGen :: (Show a, Eq a, Arbitrary a, Gen a) => a -> SomeGen 

instance Show SomeGen where 
    showsPrec p (SomeGen a) = showsPrec p a 
    show (SomeGen a) = show a 

instance Arbitrary SomeGen where 
    arbitrary = do 
    GenDict (Proxy :: Proxy a) <- arbitrary 
    a :: a <- arbitrary 
    return $ SomeGen a 
    shrink (SomeGen a) = 
    map SomeGen $ shrink a 

data GenDict where 
    GenDict :: (Show a, Eq a, Arbitrary a, Gen a) => Proxy a -> GenDict 

instance Arbitrary GenDict where 
    arbitrary = 
    elements 
    [ GenDict (Proxy :: Proxy Bool) 
    , GenDict (Proxy :: Proxy Int) 
    ] 

data Proxy a = Proxy 

してみてください。このArbitraryディクショナリインスタンスを使用して、現存する定量化された値に対してArbitraryのインスタンスを定義します。

もう1つの例はhttps://github.com/sonyandy/var/blob/4e0b12c390eb503616d53281b0fd66c0e1d0594d/tests/properties.hs#L217です。

ConstraintKindsを使用する場合は、これをさらに一般化することができます(また、定型文を減らすこともできます)。以下は一度だけ定義されています。テストしたい各タイプのクラスについては

data Some c where 
    Some :: (Show a, Arbitrary a, c a) => a -> Some c 

instance Show (Some c) where 
    showsPrec p (Some a) = showsPrec p a 
    show (Some a) = show a 

instance Arbitrary (Dict c) => Arbitrary (Some c) where 
    arbitrary = do 
    Dict (Proxy :: Proxy a) :: Dict c <- arbitrary 
    a :: a <- arbitrary 
    return $ Some a 
    shrink (Some a) = 
    map Some $ shrink a 

data Dict c where 
    Dict :: (Show a, Arbitrary a, c a) => Proxy a -> Dict c 

data Proxy a = Proxy 

class (c a, d a) => (c &&# d) a 
instance (c a, d a) => (c &&# d) a 

DictArbitraryインスタンスが必要です。輸入

instance Arbitrary (Dict (Eq &&# Gen)) where 
    arbitrary = 
    elements 
    [ Dict (Proxy :: Proxy Bool) 
    , Dict (Proxy :: Proxy Int) 
    ] 

np_prop :: Some (Eq &&# Gen) -> Bool 
np_prop (Some a) = prev (next a) == a 
関連する問題