2012-04-06 28 views
15

Tôi vừa bắt đầu sử dụng QuickCheck với một loạt mã Haskell. Tôi biết đằng sau thời gian, tôi biết. Câu hỏi này là hai phiên bản:Thực hành tốt nhất của Haskell QuickCheck (đặc biệt là khi thử nghiệm loại lớp học)

Thứ nhất, các phương pháp hay nhất chung để kiểm tra nhanh là gì? Cho đến nay, tôi đã nhặt như sau:

  • Tên của bạn kiểm tra prop_ * (khó chịu, bởi vì mọi thứ khác là camelCase)
  • Kiểm tra xuất khẩu mã (nếu bạn đang thử nghiệm internals bạn đang có khả năng làm việc đó sai)
  • tính thử nghiệm, không ví dụ
    • Đừng nói X is out of range, Y is in range
    • Thay vào đó, hãy nói if x is out of range, normalize x ≠ x (hoặc một số tài sản đó khác)

Nhưng tôi vẫn đang nắm bắt các phương pháp hay nhất khác. Cụ thể:

  • Thuộc tính được lưu giữ ở đâu?
    • Cùng một tệp?
    • trong thư mục test/? (Nếu có, thì làm cách nào để bạn nhập nội dung trong số src/?)
    • trong thư mục Properties/ dưới src?

Quan trọng nhất, chúng ta có xu hướng thử nghiệm các thuộc tính trên các loại lớp như thế nào? Ví dụ: hãy xem xét loại lớp sau (đơn giản):

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

Tôi muốn kiểm tra thuộc tính ∀ x: prev (next x) == x. Tất nhiên, điều này liên quan đến việc viết các bài kiểm tra cho mỗi trường hợp. Thật tẻ nhạt khi viết cùng một thuộc tính cho mỗi cá thể, đặc biệt khi thử nghiệm phức tạp hơn. Cách tiêu chuẩn để khái quát hóa các xét nghiệm như thế nào?

+4

Đối với hàng nhập khẩu khi sử dụng song song 'src/'và thử nghiệm /' thư mục ', bạn sẽ muốn đặt' Hs-Source-dirs: src, test' trong file' .cabal' của bạn để cả hai thư mục nằm trong đường dẫn tìm kiếm mô-đun. – hammar

+2

Tại sao nội bộ không thể có thuộc tính? – alternative

+0

Họ chắc chắn có thể, đó chỉ là khó khăn hơn để có được các xét nghiệm để họ và (theo kinh nghiệm của tôi) nó hữu ích hơn nhiều để kiểm tra hành vi xuất khẩu thay vì chi tiết thực hiện. – So8res

Trả lời

10

Tôi tin rằng quy ước prop_ đến từ QC đến với tập lệnh chạy tất cả các chức năng bắt đầu bằng prop_ làm thử nghiệm. Vì vậy, không có lý do thực sự để làm như vậy, nhưng nó hiện nổi bật trực quan (do đó, tài sản cho một chức năng fooprop_foo).

Và không có gì sai khi kiểm tra nội bộ.Có hai cách để làm như vậy:

  • Đặt các thuộc tính trong mô-đun giống như internals. Điều này làm cho các mô-đun lớn hơn, và đòi hỏi một sự phụ thuộc vô điều kiện về QC cho dự án (trừ khi bạn sử dụng CPP hackery).

  • Có internals trong một mô-đun không xuất khẩu, với các chức năng để thực sự được xuất khẩu tái xuất khẩu từ mô-đun khác. Sau đó, bạn có thể nhập mô-đun nội bộ vào mô-đun xác định thuộc tính QC và mô-đun đó chỉ được xây dựng (và có phụ thuộc QC) nếu một cờ được chỉ định trong tệp .cabal được sử dụng.

Nếu dự án của bạn là lớn, sau đó có riêng src/test/ thư mục có thể hữu ích (mặc dù có một sự phân biệt có thể ngăn cản bạn từ internals thử nghiệm). Nhưng nếu dự án của bạn không phải là tất cả những gì lớn (và nằm trong một hệ thống phân cấp mô-đun tổng thể anyway), sau đó không có nhu cầu thực sự để chia nó lên như thế.

Như Norman Ramsey đã nói trong câu trả lời của mình, đối với các loại lớp, bạn chỉ có thể xác định thuộc tính là trên typeclass và sử dụng tương ứng.

+0

Something chúng ta thường làm là phải có một Cabal 'phần thử suite' trực tiếp phụ thuộc vào các module bên trong (chứ không phải là thư viện được định nghĩa trong tập tin .cabal giống nhau) và cách mà bạn có thể kiểm tra những với chi phí của một số biên soạn thêm thời gian. – tibbe

15

Đó là tẻ nhạt để viết các tài sản tương tự cho mỗi trường hợp

Bạn không làm điều này. Bạn viết bất động sản một lần cho các lớp:

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 

Sau đó, để kiểm tra nó, bạn cast một loại đặc biệt:

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

câu hỏi khác của bạn tôi không thể giúp đỡ với.

3

Hãy thử

{-# 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 

Lớp loại được reified thành một điển existentially định lượng, trên đó một thể hiện Arbitrary được định nghĩa. Arbitrary dụ điển này sau đó được sử dụng để xác định một thể hiện của Arbitrary cho các giá trị existentially định lượng.

Một ví dụ khác được đưa ra tại https://github.com/sonyandy/var/blob/4e0b12c390eb503616d53281b0fd66c0e1d0594d/tests/properties.hs#L217.

Điều này có thể được tổng quát hơn nữa (và giảm bản đồ lò hơi) nếu bạn sẵn sàng sử dụng ConstraintKinds. Sau đây được định nghĩa chỉ một lần.

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 

Đối với mỗi lớp loại mà bạn muốn kiểm tra, một thể hiện Arbitrary của Dict là bắt buộc.

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 
Các vấn đề liên quan