Могу ли я генерировать произвольные строки и не повторять спецификации в QuickCheck?

Дано

data MyType = MyType ...

makeMyType :: String -> String -> String -> MyType
-- ...

type StringThing = String

где строки, которые makeMyType ожидает (соответственно):

  • строка с разделителями - из некоторых пользовательских строк (например, "Hilary-Jeb-David-Charles"),
  • строка из 4 заглавных букв и
  • строка целых чисел от 1 до 26, разделенная ., каждое из которых дополнено символами от нуля до двух (например, "04.23.11.09")

Я могу использовать QuickCheck для создания адекватных тестов с чем-то вроде

{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
import Test.QuickCheck
import Data.List (intercalate)
import Text.Printf (printf)

-- This really should be an arbitrary string of random length in some range
instance Arbitrary StringThing where
        arbitrary = elements ["FUSHFJSHF","KLSJDHFLSKJDHFLSKJDFHLSKJDHFLSKJOIWURURW","GHSHDHUUUHHHA"]

instance Arbitrary MyType where
        arbitrary = do
                -- This repetition feels unnecessary
                w1 <- elements ['A'..'Z']
                w2 <- elements ['A'..'Z']
                w3 <- elements ['A'..'Z']
                w4 <- elements ['A'..'Z']
                c1 <- elements someListOfCustomStuff
                c2 <- elements someListOfCustomStuff
                c3 <- elements someListOfCustomStuff
                c4 <- elements someListOfCustomStuff
                r1 <- choose (1,26)
                r2 <- choose (1,26)
                r3 <- choose (1,26)
                r4 <- choose (1,26)
                return $ makeMyType (intercalate "-" [c4,c3,c2,c1])
                                    [w1,w2,w3,w4]
                                    (intercalate "." $ (printf "%02d") <$> ([r1,r2,r3,r4] :: [Int]))

prop_SomeProp :: MyType -> StringThing -> Bool
prop_SomeProp mt st = ...

Но StringThing действительно должен принимать произвольные строки заглавных букв случайной длины в пределах некоторого диапазона, и повторение одной и той же спецификации для всех w..., c.. и r.... кажется ненужным.

Есть ли в QuickCheck способ:

  1. генерировать случайные строки с длиной в некоторых пределах, ограниченные определенными символами, и
  2. «поделиться» спецификацией, используя elements или choose среди нескольких значений?

person orome    schedule 11.11.2015    source источник
comment
чтобы сгенерировать [w1, ..., w4], вы можете использовать vectorOf 4 (elements ['A'..'Z']) вместо w1 <- ...   -  person behzad.nouri    schedule 12.11.2015
comment
vectorOf является синонимом replicateM, который является более общим комбинатором. Не уверен, как он пробрался (это датируется временем, когда у QC не было экземпляра Monad?)   -  person drquicksilver    schedule 12.11.2015


Ответы (1)


да. Haskell отлично справляется с факторингом! Вы, безусловно, можете называть и совместно использовать подвыражения, такие как elements ['A'..'Z']

capitals = elements ['A'..'Z']

instance Arbitrary StringThing where
        arbitrary = do
          l <- choose (1,50) -- this is your string length
          replicateM l capitals

И для вашего MyType вы также можете довольно часто использовать replicateM:

instance Arbitrary MyType where
        arbitrary = do
                ws <- replicateM 4 capitals
                cs <- replicateM 4 (elements someListOfCustomStuff)
                rs <- replicateM 4 (choose (1,26))
                return $ makeMyType (intercalate "-" cs)
                                    ws
                                    (intercalate "." $ (printf "%02d") <$> (rs :: [Int]))
person drquicksilver    schedule 11.11.2015
comment
Дох! — Я совсем забыл про replicateM! - person orome; 12.11.2015