This is a library of fuzzers you can use to supply values to your fuzz tests. You can typically pick out which ones you need according to their types.
A Fuzzer a knows how to create values of type a in two different ways. It
can create them randomly, so that your test's expectations are run against many
values. Fuzzers will often generate edge cases likely to find bugs. If the
fuzzer can make your test fail, it also knows how to "shrink" that failing input
into more minimal examples, some of which might also cause the tests to fail. In
this way, fuzzers can usually find the smallest or simplest input that
reproduces a bug.
A fuzzer for int values. It will never produce NaN, Infinity, or -Infinity.
It's possible for this fuzzer to generate any 32-bit integer, but it favors numbers between -50 and 50 and especially zero.
A fuzzer for int values within between a given minimum and maximum value, inclusive. Shrunken values will also be within the range.
Remember that Random.maxInt
is the maximum possible int value, so you can do intRange x Random.maxInt to get all
the ints x or bigger.
A fuzzer for float values. It will never produce NaN, Infinity, or -Infinity.
It's possible for this fuzzer to generate any other floating-point value, but it favors numbers between -50 and 50, numbers between -1 and 1, and especially zero.
A fuzzer for float values within between a given minimum and maximum value, inclusive. Shrunken values will also be within the range.
A fuzzer for percentage values. Generates random floats between 0.0 and
1.0. It will test zero and one about 10% of the time each.
Generates random printable ASCII strings of up to 1000 characters.
Shorter strings are more common, especially the empty string.
Given a fuzzer of a type, create a fuzzer of a maybe for that type.
Given fuzzers for an error type and a success type, create a fuzzer for a result.
Given a fuzzer of a type, create a fuzzer of a list of that type. Generates random lists of varying length, favoring shorter lists.
Given a fuzzer of a type, create a fuzzer of an array of that type. Generates random arrays of varying length, favoring shorter arrays.
The representation of fuzzers is opaque. Conceptually, a Fuzzer a
consists of a way to randomly generate values of type a, and a way to shrink
those values.
Create a fuzzer that only and always returns the value provided, and performs no shrinking. This is hardly random, and so this function is best used as a helper when creating more complicated fuzzers.
Map a function over a fuzzer. This applies to both the generated and the shrunken values.
Map over two fuzzers.
Map over three fuzzers.
Map over four fuzzers.
Map over five fuzzers.
Map over many fuzzers. This can act as mapN for N > 5.
The argument order is meant to accommodate chaining:
map f aFuzzer
|> andMap anotherFuzzer
|> andMap aThirdFuzzerNote that shrinking may be better using mapN.
Create a fuzzer based on the result of another fuzzer.
Create a new Fuzzer by providing a list of probabilistic weights to use
with other fuzzers.
For example, to create a Fuzzer that has a 1/4 chance of generating an int
between -1 and -100, and a 3/4 chance of generating one between 1 and 100,
you could do this:
Fuzz.frequency
[ ( 1, Fuzz.intRange -100 -1 )
, ( 3, Fuzz.intRange 1 100 )
]There are a few circumstances in which this function will return an invalid fuzzer, which causes it to fail any test that uses it:
Be careful recursively using this fuzzer in its arguments. Often using map
is a better way to do what you want. If you are fuzzing a tree-like data
structure, you should include a depth limit so to avoid infinite recursion, like
so:
type Tree
= Leaf
| Branch Tree Tree
tree : Int -> Fuzzer Tree
tree i =
if i <= 0 then
Fuzz.constant Leaf
else
Fuzz.frequency
[ ( 1, Fuzz.constant Leaf )
, ( 2, Fuzz.map2 Branch (tree (i - 1)) (tree (i - 1)) )
]Conditionally filter a fuzzer to remove occasional undesirable input. Takes a limit for how many retries to attempt, and a fallback function to, if no acceptable input can be found, create one from an unacceptable one. Also takes a condition to determine if the input is acceptable or not, and finally the fuzzer itself.
A good number of max retires is ten. A large number of retries might blow the stack.
Instead of using a tuple, consider using fuzzN.
Turn a tuple of fuzzers into a fuzzer of tuples.
Turn a 3-tuple of fuzzers into a fuzzer of 3-tuples.
Turn a 4-tuple of fuzzers into a fuzzer of 4-tuples.
Turn a 5-tuple of fuzzers into a fuzzer of 5-tuples.
Build a custom Fuzzer a by providing a Generator a and a Shrinker a.
Generators are defined in mgold/elm-random-pcg,
which is not core's Random module but has a compatible interface. Shrinkers are
defined in elm-community/shrink.
Here is an example for a record:
import Random.Pcg as Random
import Shrink
type alias Position =
{ x : Int, y : Int }
position : Fuzzer Position
position =
Fuzz.custom
(Random.map2 Position (Random.int -100 100) (Random.int -100 100))
(\{ x, y } -> Shrink.map Position (Shrink.int x) |> Shrink.andMap (Shrink.int y))Here is an example for a custom union type, assuming there is already a genName : Generator String defined:
type Question
= Name String
| Age Int
question =
let
generator =
Random.bool
|> Random.andThen
(\b ->
if b then
Random.map Name genName
else
Random.map Age (Random.int 0 120)
)
shrinker question =
case question of
Name n ->
Shrink.string n |> Shrink.map Name
Age i ->
Shrink.int i |> Shrink.map Age
in
Fuzz.custom generator shrinkerIt is not possible to extract the generator and shrinker from an existing fuzzer.
A fuzzer for char values. Generates random ascii chars disregarding the control characters.
A fuzzer for the unit value. Unit is a type with only one value, commonly used as a placeholder.
A fuzzer for order values.
A fuzzer that is invalid for the provided reason. Any fuzzers built with it are also invalid. Any tests using an invalid fuzzer fail.
A fuzzer for bool values.