Controlling how test data is generated in QuickCheck

南楼画角 提交于 2019-11-30 04:48:43

One thing that QuickCheck 2 introduced that could be a source of inefficiency is the shrink function. If a property fails, then the shrink function is called which gives candidates on smaller test values, and they are shrunk until they cannot give a smaller value for which the property fails. But this only happens when properties fail.

The changes for the arbitrary instance for lists has not changed much between version 1 and version 2. The difference is in wording, version 1 uses vector, and version 2 uses a list comprehension, but then vector is implemented with exactly such a list comprehension of sequenced calls to arbitrary.

Both implementations used the size parameter. In QuickCheck 1, the size of a generated value is by default div n 2 + 3, where n is the number of the test. QuickCheck 2 uses another approach, where the maximum size and the number of tests is configured. The test sizes will be distributed in that range, growing linearly in the number of tests (see computeSize' in quickCheckWithResult). This means, with the default setting of 100 tests and maximum size, the maximum size from QuickCheck 1 would be (div 100 2 + 3) = 53, and with QuickCheck 2 it would simply be 100.

As subset sum is NP-complete you probably have an exponential algorithm ;) Then the difference in running time between a list of length 50 and 100 can of course be enormous. Understandably you want small lists to test with. You have two choices: make a new data type (preferably with newtype) or make a stand-alone generator and use forAll. By using newtype you can also specify how values should be shrunk. This is an example implementation using the newtype approach:

newtype SmallIntList = SmallIntList [Int] deriving (Eq,Show)

instance Arbitrary SmallIntList where
  arbitrary = sized $ \s -> do
                 n <- choose (0,s `min` 50)
                 xs <- vectorOf n (choose (-10000,10000))
                 return (SmallIntList xs)
  shrink (SmallIntList xs) = map SmallIntList (shrink xs)

This Arbitrary instance does not make lists longer than 50 elements. The elements do not use the size parameter, so they are always in the range [-10000,10000], so there is some room for improvement. The shrink function simply uses the available shrinks for lists and Ints.

To use this instance with your property, just use a SmallIntList as an argument:

prop_sumEqualsS (SmallIntList l) s = case subsetSum l s of
                                         ...
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!