I'm trying to write a modified Arbitrary
instance for my data type, where (in my case) a subcomponent has a type [String]
. I would ideally like to bring uniqueness in the instance itself, that way I don't need ==>
headers / prerequisites for every test I write.
Here's my data type:
data Foo = Vars [String]
and the trivial Arbitrary
instance:
instance Arbitrary Foo where
arbitrary = Vars <$> (:[]) <$> choose ('A','z')
This instance is strange, I know. In the past, I've had difficulty when quickcheck combinatorically explodes, so I'd like to keep these values small. Another request - how can I make an instance where the generated strings are under 4 characters, for instance?
All of this, fundamentally requires (boolean) predicates to augment Arbitrary
instances. Is this possible?
Definitely you want the instance to produce only instances that match the intention of the data type. If you want all the variables to be distinct, the
Arbitrary
instance must reflect this. (Another question is if in this case it wouldn't make more sense to defineVars
as a set, likenewtype Vars = Set [String]
.)I'd suggest to check for duplicates using
Set
orHashtable
, asnub
has O(n^2) complexity, which might slow down your test considerably for larger inputs. For example:Your instance then needs to create a list of list, and each list should be randomized. So instead of
(: [])
, which creates just a singleton list (and just one level), you need to calllistOf
twice:Also notice that
choose ('A', 'z')
allows to use all characters between A and z, which includes many control characters. My guess is that you rather want something likeIf you really want, you could also make
hasNoDups
O(n) using hash tables in theST
monad.Concerning limiting the size: you could always have your own parametrized functions that produce different
Gen Foo
, but I'd say in most cases it's not necessary.Gen
has it's own internal size parameter, which is increased throughout the tests (see this answer), so different sizes (as generated usinglistOf
) of lists are covered.But I'd suggest you to implement
shrink
, as this will give you much nicer counter-examples. For example, if we define (a wrong test) that tried to verify that no instance ofVar
contains 'a' in any of its variable:we'll get ugly counter-examples such as
But adding
to
Arbitrary Foo
makes the counter-example to be just