elm-community/elm-test/2.1.0

NOTE: this package is not compatible with Elm 0.19.1

Fuzz

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.

Common Fuzzers

bool : Fuzzer Bool

A fuzzer for bool values.

int : Fuzzer Int

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.

intRange : Int -> Int -> Fuzzer Int

A fuzzer for int values within between a given minimum and maximum value, inclusive. Shrunken values will also be within the range.

float : Fuzzer Float

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.

floatRange : Float -> Float -> Fuzzer Float

A fuzzer for float values within between a given minimum and maximum value, inclusive. Shrunken values will also be within the range.

percentage : Fuzzer Float

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.

string : Fuzzer String

Generates random printable ASCII strings of up to 1000 characters.

Shorter strings are more common, especially the empty string.

maybe : Fuzzer a -> Fuzzer (Maybe a)

Given a fuzzer of a type, create a fuzzer of a maybe for that type.

result : Fuzzer error -> Fuzzer value -> Fuzzer (Result error value)

Given fuzzers for an error type and a success type, create a fuzzer for a result.

list : Fuzzer a -> Fuzzer (List a)

Given a fuzzer of a type, create a fuzzer of a list of that type. Generates random lists of varying length, favoring shorter lists.

array : Fuzzer a -> Fuzzer (Array a)

Given a fuzzer of a type, create a fuzzer of an array of that type. Generates random arrays of varying length, favoring shorter arrays.

Working with Fuzzers

type alias Fuzzer a =
Fuzzer a

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.

constant : a -> Fuzzer a

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 -> b) -> Fuzzer a -> Fuzzer b

Map a function over a fuzzer. This applies to both the generated and the shruken values.

map2 : (a -> b -> c) -> Fuzzer a -> Fuzzer b -> Fuzzer c

Map over two fuzzers.

map3 : (a -> b -> c -> d) -> Fuzzer a -> Fuzzer b -> Fuzzer c -> Fuzzer d

Map over three fuzzers.

(a -> b -> c -> d -> e)
-> Fuzzer a
-> Fuzzer b
-> Fuzzer c
-> Fuzzer d
-> Fuzzer e

Map over four fuzzers.

(a -> b -> c -> d -> e -> f)
-> Fuzzer a
-> Fuzzer b
-> Fuzzer c
-> Fuzzer d
-> Fuzzer e
-> Fuzzer f

Map over five fuzzers.

andMap : Fuzzer (a -> b) -> Fuzzer a -> Fuzzer b

Map over many fuzzers. This can act as mapN for N > 5.

andThen : (a -> Fuzzer b) -> Fuzzer a -> Fuzzer b

Create a fuzzer based on the result of another fuzzer.

frequency : List ( Float, Fuzzer a ) -> Result String (Fuzzer a)

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 )
    ]

This returns a Result because it can fail in a few ways:

  • If you provide an empy list of frequencies
  • If any of the weights are less than 0
  • If the weights sum to 0

Any of these will lead to a result of Err, with a String explaining what went wrong.

frequencyOrCrash : List ( Float, Fuzzer a ) -> Fuzzer a

Calls frequency and handles Err results by crashing with the given error message.

This is useful in tests, where a crash will simply cause the test run to fail. There is no danger to a production system there.

Tuple Fuzzers

Instead of using a tuple, consider using fuzzN.

tuple : ( Fuzzer a, Fuzzer b ) -> Fuzzer ( a, b )

Turn a tuple of fuzzers into a fuzzer of tuples.

tuple3 : ( Fuzzer a, Fuzzer b, Fuzzer c ) -> Fuzzer ( a, b, c )

Turn a 3-tuple of fuzzers into a fuzzer of 3-tuples.

( Fuzzer a, Fuzzer b, Fuzzer c, Fuzzer d )
-> Fuzzer ( a, b, c, d )

Turn a 4-tuple of fuzzers into a fuzzer of 4-tuples.

( Fuzzer a, Fuzzer b, Fuzzer c, Fuzzer d, Fuzzer e )
-> Fuzzer ( a, b, c, d, e )

Turn a 5-tuple of fuzzers into a fuzzer of 5-tuples.

Uncommon Fuzzers

custom : Generator a -> Shrinker a -> Fuzzer a

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 shrinker

It is not possible to extract the generator and shrinker from an existing fuzzer.

char : Fuzzer Char

A fuzzer for char values. Generates random ascii chars disregarding the control characters.

unit : Fuzzer ()

A fuzzer for the unit value. Unit is a type with only one value, commonly used as a placeholder.

order : Fuzzer Order

A fuzzer for order values.