I have come across a few instances in my testing with QuickCheck when it would have simplified things to write my own modifiers in some cases, but I'm not exactly sure how one would do this. In particular, it would be helpful to know how to write a modifier for generators of lists and of numerics (such as Int). I'm aware of NonEmptyList, and Positive and NonNegative, that are already in the library, but in some instances it would have made my tests clearer if I could have specified something like a list that is not only NonEmpty, but also NonSingleton (so, it has at least 2 elements), or an Int that is greater than 1, not just NonZero or Positive, or an Int(egral) that is even/odd, etc.
How do you write a new modifier in QuickCheck
157 Views Asked by josiah At
1
There's plenty of way in which you can do that. Here's some examples.
Combinator function
You can write a combinator as a function. Here's one that generates non-singleton lists from any
Gen a:This has the same type as the built-in
listOffunction, and can be used in the same way:Here I've taken advantage of
Gen abeing aMonadso that I could write both the function and the property withdonotation, but you can also write it using monadic combinators if you so prefer.The function simply generates two values,
x1andx2, as well as a listxsof arbitrary size (which can be empty), and creates a list of all three. Sincex1andx2are guaranteed to be single values, the resulting list will have at least those two values.Filtering
Sometimes you just want to throw away a small subset of generated values. You can to that with the built-in
==>combinator, here used directly in a property:While this property is tautological, it demonstrates that the predicate you place to the left of
==>ensures that whatever runs on the right-hand side of==>has passed the predicate.Existing monadic combinators
Since
Gen ais aMonadinstance, you can also use existingMonad,Applicative, andFunctorcombinators. Here's one that turns any number inside of anyFunctorinto an even number:Notice that this works for any
Functor f, not just forGen a. Since, however,Gen ais aFunctor, you can still useevenInt:The
arbitraryfunction call here creates an unconstrainedIntegervalue.evenIntthen makes it even by multiplying it by two.Arbitrary newtypes
You can also use
newtypeto create your own data containers, and then make themArbitraryinstances:This also enables you to implement
shrink, if you need it.You can use the
newtypein a property like this:The
Arbitraryinstance usesarbitraryfor the typeato generate an unconstrained valuei, then doubles it and adds one, thereby ensuring that the value is odd.Take a look at the QuickCheck documentation for many more built-in combinators. I particularly find
choose,elements,oneof, andsuchThatuseful for expressing additional constraints.