qcheck

QuickCheck-inspired property-based testing with integrated shrinking

Overview

Rather than specifying test cases manually, you describe the invariants that values of a given type must satisfy (“properties”). Then, generators generate lots of values (test cases) on which the properties are checked. Finally, if a value is found for which a given property does not hold, that value is “shrunk” in order to find an nice, informative counter-example that is presented to you.

This module has functions for running and configuring property tests as well as generating random values (with shrinking) to drive those tests.

For full usage examples, see the project README.

API

Running Tests

Configuring and Seeding

Low-Level Construction

Combinators and Other Utilities

Generator Categories

There are a few different “categories” of generator.

Numeric Generators

Ints

Floats

Codepoint and String Generators

The main purpose of codepoint generators is to use them to generate strings.

Codepoints

ASCII Codepoints

Strings

String generators are built from codepoint generators.

Bit Array Generators

Bit array values come from integers, and handle sizes and shrinking in a reasonable way given that values in the bit array are connected to the size of the bit array in certain situations.

These functions will generate bit arrays that cause runtime crashes when targeting JavaScript.

Byte-aligned bit arrays

Byte-aligned bit arrays always have a size that is a multiple of 8.

These bit arrays work on the JavaScript target.

UTF-8 Encoded Bit Arrays

Bit arrays where the values are always valid utf-8 encoded bytes.

These bit arrays work on the JavaScript target.

Collection Generators

Lists

Dictionaries

Sets

Tuples

Other Generators

Debug Generators

These functions aren’t meant to be used directly in your tests. They are provided to help debug or investigate what values and shrinks that a generator produces.

Notes

The exact distributions of individual generators are considered an implementation detail, and may change without a major version update. For example, if the option generator currently produced None approximately 25% of the time, but that distribution was changed to produce None 50% of the time instead, that would not be considered a breaking change.

Types

Configuration for the property-based testing.

pub opaque type Config

A generator for producing random values of type a.

While the type is not opaque, it’s recommended to use the provided generators, and the provided combinators like map and bind. Direct generator construction should be reserved for special use-cases.

pub type Generator(a) {
  Generator(fn(Seed) -> #(Tree(a), Seed))
}

Constructors

  • Generator(fn(Seed) -> #(Tree(a), Seed))

A type representing a seed value used to initialize random generators.

pub type Seed =
  random.Seed

Functions

pub fn alphabetic_ascii_codepoint() -> Generator(UtfCodepoint)

Generate alphabetic ASCII characters.

Returns

A generator that produces alphabetic ASCII characters as codepoints

Example

string_from(alphabetic_ascii_codepoint())
pub fn alphanumeric_ascii_codepoint() -> Generator(UtfCodepoint)

Generate alphanumeric ASCII characters.

Returns

A generator that produces alphanumeric ASCII characters as codepoints

Example

string_from(alphanumeric_ascii_codepoint())
pub fn apply(
  f: Generator(fn(a) -> b),
  x: Generator(a),
) -> Generator(b)

Support for constructing generators in an applicative style.

Example

type Box {
  Box(x: Int, y: Int, w: Int, h: Int)
}

fn box_generator() {
  return({
    use x <- parameter
    use y <- parameter
    use w <- parameter
    use h <- parameter
    Box(x:, y:, w:, h:)
  })
  |> apply(bounded_int(-100, 100))
  |> apply(bounded_int(-100, 100))
  |> apply(bounded_int(1, 100))
  |> apply(bounded_int(1, 100))
}
pub fn ascii_digit_codepoint() -> Generator(UtfCodepoint)

Generate ASCII digits as codepoints.

Returns

A generator that produces ASCII digits from 0 to 9 as codepoints

Example

string_from(ascii_digit_codepoint())
pub fn ascii_whitespace_codepoint() -> Generator(UtfCodepoint)

Generate ASCII whitespace as codepoints.

Returns

A generator that produces ASCII whitespace as codepoints

Example

let whitespace_generator = string_from(ascii_whitespace_codepoint())
pub fn bind(
  generator: Generator(a),
  f: fn(a) -> Generator(b),
) -> Generator(b)

Transform a generator by applying a function that returns another generator to each generated value.

Unlike map, this allows for a dependency on the resulting generator and the original generated values.

Arguments

  • generator: A generator that creates a value of type a
  • f: A function that takes a value of type a and returns a generator of type b

Returns

A generator that first generates a value of type a, then uses that value to generate a value of type b

Examples

Say you wanted to generate a valid date in string form, like "2025-01-30". In order to generate a valid day, you need both the month (some months have 31 days, other have fewer) and also the year (since the year affects the max days in February). So, before you can generate a valid day, you must first generate a year and a month. You could imagine a set of functions like this:

fn date_generator() -> Generator(String) {
  use #(year, month) <- bind(tuple2(year_generator(), month_generator()))
  use day <- map(day_generator(year:, month:))

  int.to_string(year)
  <> "-"
  <> int.to_string(month)
  <> "-"
  <> int.to_string(day)
}

// Note how the day generator depends on the value of `year` and `month`.
fn day_generator(year year, month month) -> Generator(Int) {
  todo
}

fn year_generator() -> Generator(Int) {
  todo
}

fn month_generator() -> Generator(Int) {
  todo
}

Another situation in which you would need bind is if you needed to generate departure and arrival times. We will say a pair of departure and arrival times is valid if the departure time is before the arrival time. That means we cannot generate an arrival time without first generating a departure time. Here is how that might look:

fn departure_and_arrival_generator() {
  use departure_time <- bind(departure_time_generator())
  use arrival_time <- map(arrival_time_generator(departure_time))
  #(departure_time, arrival_time)
}

fn departure_time_generator() {
  todo
}

fn arrival_time_generator(departure_time) {
  todo
}
pub fn bit_array() -> Generator(BitArray)

Generate bit arrays.

Warning

This function will generate bit arrays that cause runtime crashes when targeting JavaScript.

pub fn bool() -> Generator(Bool)

Generate boolean values.

Returns

A generator that generates boolean values and shrinks towards False

pub fn bounded_codepoint(
  from low: Int,
  to high: Int,
) -> Generator(UtfCodepoint)

Generate Unicode codepoints uniformly distributed within a specified range.

Arguments

  • from: Minimum codepoint value (inclusive)
  • to: Maximum codepoint value (inclusive)

Returns

A generator that produces Unicode codepoints within the specified range.

Notes

  • If the range is invalid, it will be automatically adjusted to a valid range
  • Shrinks towards an origin codepoint (typically lowercase ‘a’)
  • Mainly used for string generation

Example

let cyrillic_character = bounded_codepoint(from: 0x0400, to: 0x04FF)
pub fn bounded_float(
  from low: Float,
  to high: Float,
) -> Generator(Float)

Generate floats uniformly distributed between from and to, inclusive.

Arguments

  • from: Lower bound of the range (inclusive)
  • to: Upper bound of the range (inclusive)

Returns

A generator producing floats within the specified range.

Behavior

  • Shrinks towards 0, but won’t shrink outside of the range [from, to]
  • Automatically orders parameters if from > to

Example

Generate floats between -10 and 10.

bounded_float(-10, 10)
pub fn bounded_int(from low: Int, to high: Int) -> Generator(Int)

Generate integers uniformly distributed between from and to, inclusive.

Arguments

  • from: Lower bound of the range (inclusive)
  • to: Upper bound of the range (inclusive)

Returns

A generator producing integers within the specified range.

Behavior

  • Shrinks towards 0, but won’t shrink outside of the range [from, to]
  • Automatically orders parameters if from > to

Example

Generate integers between -10 and 10.

bounded_int(-10, 10)
pub fn byte_aligned_bit_array() -> Generator(BitArray)

Generate byte-aligned bit arrays.

pub fn codepoint() -> Generator(UtfCodepoint)

Generate Unicode codepoints with a decent distribution that is good for generating genreal strings.

Returns

A generator that produces Unicode codepoints

Example

The decent default string generator could be writen something like this:

generic_string(codepoint(), small_non_negative_int())
pub fn codepoint_from_ints(
  first: Int,
  rest: List(Int),
) -> Generator(UtfCodepoint)

Generate a codepoint from a list of codepoints represented as integers.

Splitting up the arguments in this way ensures some value is always generated by preventing you from passing in an empty list.

Arguments

  • first: First codepoint to choose from
  • rest: Additional codepoints to choose from

Returns

A Generator that produces codepoints from the provided values

Example

let ascii_whitespace_generator = codepoint_from_ints(
  // Horizontal tab
  9,
  [
    // Line feed
    10,
    // Vertical tab
    11,
    // Form feed
    12,
    // Carriage return
    13,
    // Space
    32,
  ],
)
pub fn codepoint_from_strings(
  first: String,
  rest: List(String),
) -> Generator(UtfCodepoint)

Generate a codepoint from a list of strings.

Arguments

  • first: First character to choose from
  • rest: Additional characters to choose from

Returns

A Generator that produces codepoints from the provided values

Notes

  • Splitting up the arguments in this way ensures some value is always generated by preventing you from passing in an empty list.
  • Only the first codepoint is taken from each of the provided strings

Example

let quadrant_generator = codepoint_from_strings("▙", ["▛", "▜", "▟"])
pub fn config(
  test_count test_count: Int,
  max_retries max_retries: Int,
  seed seed: Seed,
) -> Config

Create a new Config with specified test count, max retries, and seed.

Arguments

  • test_count: Number of test cases to generate
  • max_retries: Maximum retries to test a shrunk input candidate. Values > 1 can be useful for testing non-deterministic code.
  • seed: Random seed for deterministic test generation

Returns

A Config with the provided settings, using defaults for any invalid arguments

Example

let config = config(test_count: 10_000, max_retries: 1, seed: seed(47))
pub fn constant(a: a) -> Generator(a)

Create a generator that always returns the same value and does not shrink.

Arguments

  • a: The value to be consistently generated

Returns

A Generator that produces the same input for all test cases

Example

use string <- given(constant("Gleam"))
string == "Gleam"

Notes

This function is an alias for return.

pub fn default_config() -> Config

Create a default configuration for property-based testing.

Returns

  • A Config with default settings for test count, max retries, and seed

Example

let config = default_config()
pub fn fixed_length_list_from(
  element_generator: Generator(a),
  length: Int,
) -> Generator(List(a))

Generate fixed-length lists with elements from the given generator.

Arguments

  • element_generator: Generates list elements
  • length: The length of the generated lists

Returns

A generator that produces fixed-length lists with elements from the given generator.

Shrinking

Shrinks first on list length, then on list elements, ensuring shrunk lists remain within length generator’s range.

Example

fixed_length_list_from(string(), 5)
pub fn fixed_length_string_from(
  generator: Generator(UtfCodepoint),
  length: Int,
) -> Generator(String)

Generate a fixed-length string from the given codepoint generator.

Arguments

  • generator: A generator for codepoints
  • length: Number of graphemes in the generated string

Returns

A generator that produces strings with the specified number of graphemes

Example

fixed_length_string_from(codepoint(), 5)
pub fn fixed_size_bit_array(size: Int) -> Generator(BitArray)

Generate fixed-size bit arrays.

Warning

This function will generate bit arrays that cause runtime crashes when targeting JavaScript.

pub fn fixed_size_bit_array_from(
  value_generator: Generator(Int),
  bit_size: Int,
) -> Generator(BitArray)

Generate fixed-size bit arrays where elements are generated using the provided integer generator.

Arguments

  • value_generator: Generators bit array values
  • bit_size: Number of bits in the generated bit array

Returns

A generator of fixed-size bit arrays

Notes

Shrinks on values, not on size

Example

fixed_size_bit_array_from(bounded_int(0, 255), 64)

Warning

This function will generate bit arrays that cause runtime crashes when targeting JavaScript unless the bit size is a multiple of 8.

pub fn fixed_size_byte_aligned_bit_array(
  num_bytes: Int,
) -> Generator(BitArray)

Generate byte-aligned bit arrays of the given number of bytes

Arguments

  • num_bytes: Number of bytes for the generated bit array

Returns

A generator that produces bit arrays with the specified number of bytes

Example

Generate 4-byte bit arrays:

fixed_size_byte_aligned_bit_array(4)
pub fn fixed_size_byte_aligned_bit_array_from(
  value_generator: Generator(Int),
  byte_size: Int,
) -> Generator(BitArray)

Generate byte-aligned bit arrays of the given number of bytes from the given value generator

Arguments

  • value_generator: Generates the values of the bit array
  • num_bytes: Number of bytes for the generated bit array

Returns

A generator that produces bit arrays with the specified number of bytes according to the given value generator

Example

Generate 4-byte bit arrays:

fixed_size_byte_aligned_bit_array(bounded_int(0, 255), 16)
pub fn fixed_size_utf8_bit_array(
  num_codepoints: Int,
) -> Generator(BitArray)

Generate a fixed-sized bit array of valid UTF-8 encoded bytes with the given number of codepoints.

Arguments

  • num_codepoints: The number of Unicode codepoints represented by the generated bit arrays

Returns

A generator that produces of fixed-sized bit arrays of UTF-8 encoded bytes

Details

  • The size is determined by the number of Unicode codepoints, not bytes or bits.
  • If a negative number is provided, it is converted to zero.
pub fn fixed_size_utf8_bit_array_from(
  codepoint_generator: Generator(UtfCodepoint),
  num_codepoints: Int,
) -> Generator(BitArray)

Generate a fixed-sized bit array of valid UTF-8 encoded bytes with the given number of codepoints and values generated from the given codepoint generator.

Arguments

  • codepoint_generator: Generates the values
  • num_codepoints: The number of Unicode codepoints represented by the generated bit arrays

Returns

A generator that produces of fixed-sized bit arrays of UTF-8 encoded bytes

Details

  • The size is determined by the number of Unicode codepoints, not bytes or bits.
  • If a negative number is provided, it is converted to zero.
pub fn float() -> Generator(Float)

Generate floats with a bias towards smaller values.

Shrinks towards 0.0.

Returns

A generator that produces floating-point numbers

pub fn from_generators(
  generator: Generator(a),
  generators: List(Generator(a)),
) -> Generator(a)

Choose a generator from a list of generators, then generate a value from the selected generator.

Arguments

  • generator: Initial generator to include in the choice
  • generators: Additional generators to choose from

Returns

A generator that selects and uses one of the provided generators

Notes

  • Will always produce values since at least one generator is required

Example

fn mostly_ascii_characters_generator() {
  from_generators(uppercase_ascii_codepoint(), [
    lowercase_ascii_codepoint(),
    uniform_codepoint(),
  ])
}
pub fn from_weighted_generators(
  generator: #(Int, Generator(a)),
  generators: List(#(Int, Generator(a))),
) -> Generator(a)

Choose a generator from a list of weighted generators, then generate a value from the selected generator.

Arguments

  • generator: Initial weighted generator (weight and generator)
  • generators: Additional weighted generators

Returns

A generator that selects and generates values based on the provided weights

Example

from_weighted_generators(#(26, uppercase_ascii_codepoint()), [
  #(26, lowercase_ascii_codepoint()),
  #(10, ascii_digit_codepoint()),
])
pub fn generate(
  generator: Generator(a),
  number_to_generate: Int,
  seed: Seed,
) -> #(List(a), Seed)

Generate a fixed number of random values from a generator.

Arguments

  • generator: The generator to use for creating values
  • number_to_generate: Number of values to generate
  • seed: Random seed for value generation

Returns

A list of generated values, without their associated shrinks

Notes

Primarily useful for debugging generator behavior

pub fn generate_tree(
  generator: Generator(a),
  seed: Seed,
) -> #(Tree(a), Seed)

Generate a single value and its shrink tree from a generator.

Arguments

  • generator: The generator to use for creating the value
  • seed: Random seed for value generation

Returns

A tuple containing the generated value’s shrink tree and the next seed

Notes

Primarily useful for debugging generator behavior

pub fn generator(
  random_generator: Generator(a),
  tree: fn(a) -> Tree(a),
) -> qcheck.Generator(a)

Create a new generator from a random generator and a shrink tree function.

Arguments

  • random_generator: Produces random values of type a
  • tree: Function that creates a shrink tree for generated values

Returns

A new Generator(a) that combines random generation and shrinking

Notes

This is a low-level function for building custom generators. Prefer using built-in generators or combinators like map, bind, etc.

pub fn generic_bit_array(
  values_from value_generator: Generator(Int),
  bit_size_from bit_size_generator: Generator(Int),
) -> Generator(BitArray)

Generate bit arrays with configurable values and bit sizes.

Arguments

  • values_from: Generator for bit array contents
  • bit_size_from: Generator for bit array size

Returns

A bit array generator

Example

let generator = generic_bit_array(
  value_generator: bounded_int(0, 255),
  bit_size_generator: bounded_int(32, 64)
)

Warning

This function will generate bit arrays that cause runtime crashes when targeting JavaScript.

pub fn generic_byte_aligned_bit_array(
  values_from value_generator: Generator(Int),
  byte_size_from byte_size_generator: Generator(Int),
) -> Generator(BitArray)

Generate byte-aligned bit arrays according to the given value generator and byte size generator.

Arguments

  • value_generator: Generates the values of the bit array
  • byte_size_generator: Generates the number of bytes of the bit array

Returns

A byte-aligned bit array generator

pub fn generic_dict(
  keys_from key_generator: Generator(a),
  values_from value_generator: Generator(b),
  size_from size_generator: Generator(Int),
) -> Generator(Dict(a, b))

Generates dictionaries with keys from a key generator, values from a value generator, and sizes from a size generator.

Arguments

  • keys_from: Generator for dictionary keys
  • values_from: Generator for dictionary values
  • size_from: Generator for dictionary size

Returns

A generator that produces dictionaries

Notes

  • The actual size may be less than the generated size due to potential key duplicates
  • Shrinks on size first, then on individual elements

Example

generic_dict(
  key_generator: uniform_int(),
  value_generator: string(),
  size_generator: small_strictly_positive_int()
)
pub fn generic_list(
  elements_from element_generator: Generator(a),
  length_from length_generator: Generator(Int),
) -> Generator(List(a))

Generate lists with elements from one generator and lengths from another.

Arguments

  • elements_from: Generates list elements
  • length_from: Generates list lengths

Returns

A generator that produces lists with:

  • Elements from elements_from
  • Lengths from length_from

Shrinking

Shrinks first on list length, then on list elements, ensuring shrunk lists remain within length generator’s range.

Example

generic_list(string(), small_non_negative_int())
pub fn generic_set(
  elements_from element_generator: Generator(a),
  size_from size_generator: Generator(Int),
) -> Generator(Set(a))

Generates sets with values from an element generator, and sizes from a size generator.

Arguments

  • elements_from: Generator for set elements
  • size_from: Generator for set size

Returns

A generator that produces sets

Notes

  • The actual size may be less than the generated size due to potential duplicates
  • Shrinks on size first, then on individual elements

Example

generic_set(
  value_generator: string(),
  size_generator: small_strictly_positive_int()
)
pub fn generic_string(
  codepoints_from codepoint_generator: Generator(UtfCodepoint),
  length_from length_generator: Generator(Int),
) -> Generator(String)

Generate a string from the given codepoint generator and the given length generator.

Arguments

  • codepoint_generator: A generator for codepoints
  • length_generator: A generator to determine number of graphemes in the generated strings

Returns

A string generator

Example

generic_string(ascii_digit_codepoint(), bounded_int(8, 15))
pub fn generic_utf8_bit_array(
  codepoints_from codepoint_generator: Generator(UtfCodepoint),
  codepoint_size_from num_codepoints_generator: Generator(Int),
) -> Generator(Generator(List(UtfCodepoint)))

Generate bit arrays of UTF-8 encoded bytes with configurable values and number of codepoints.

Arguments

  • codepoints_from: Generates the codepoint values of the resulting bit arrays
  • codepoint_size_from: Generates sizes in number of codepoints represented by the resulting bit arrays

Returns

A generator of bit arrays of valid UTF-8 encoded bytes

pub fn given(
  generator: Generator(a),
  property: fn(a) -> Nil,
) -> Nil

Test a property against generated test cases using the default configuration.

Arguments

  • generator: Creates test inputs
  • property: The property to verify

Returns

  • Nil if all test cases pass (the property returns Nil)
  • Panics if any test case fails (the property panics)
pub fn list_from(
  element_generator: Generator(a),
) -> Generator(List(a))

Generate lists with elements from the given generator and the default length generator.

Arguments

  • element_generator: Generates list elements

Returns

A generator that produces lists with elements from the given generator

Shrinking

Shrinks first on list length, then on list elements.

Example

list_from(string())
pub fn lowercase_ascii_codepoint() -> Generator(UtfCodepoint)

Generate lowercase ASCII letters.

Returns

A generator that produces lowercase letters from a to z as codepoints

Example

string_from(lowercase_ascii_codepoint())
pub fn map(
  generator: Generator(a),
  f: fn(a) -> b,
) -> Generator(b)

Transform a generator by applying a function to each generated value.

Arguments

  • generator: The original generator to transform
  • f: Function to apply to each generated value

Returns

A new generator that produces values transformed by f, with shrinking behavior derived from the original generator

Examples

let even_number_generator = map(uniform_int(), fn(n) { 2 * n })

With use:

let even_number_generator = {
  use n <- map(uniform_int())
  2 * n
}
pub fn map2(
  g1: Generator(a),
  g2: Generator(b),
  f: fn(a, b) -> c,
) -> Generator(c)

Transform two generators by applying a function to their generated values.

Arguments

  • g1: First generator to provide input
  • g2: Second generator to provide input
  • f: Function to apply to generated values from g1 and g2

Returns

A new generator that produces values by applying f to values from g1 and g2

Example

use year, month <- map2(bounded_int(0, 9999), bounded_int(1, 12))
int.to_string(year) <> "-" <> int.to_string(month)
pub fn map3(
  g1: Generator(a),
  g2: Generator(b),
  g3: Generator(c),
  f: fn(a, b, c) -> d,
) -> Generator(d)

Transform three generators by applying a function to their generated values.

See docs for map2.

pub fn map4(
  g1: Generator(a),
  g2: Generator(b),
  g3: Generator(c),
  g4: Generator(d),
  f: fn(a, b, c, d) -> e,
) -> Generator(e)

Transform four generators by applying a function to their generated values.

See docs for map2.

pub fn map5(
  g1: Generator(a),
  g2: Generator(b),
  g3: Generator(c),
  g4: Generator(d),
  g5: Generator(e),
  f: fn(a, b, c, d, e) -> f,
) -> Generator(f)

Transform five generators by applying a function to their generated values.

See docs for map2.

pub fn map6(
  g1: Generator(a),
  g2: Generator(b),
  g3: Generator(c),
  g4: Generator(d),
  g5: Generator(e),
  g6: Generator(f),
  f: fn(a, b, c, d, e, f) -> g,
) -> Generator(g)

Transform six generators by applying a function to their generated values.

See docs for map2.

pub fn nil() -> Generator(Nil)

Generate a constant Nil value.

Returns

A Generator that always returns Nil and does not shrink

pub fn non_empty_bit_array() -> Generator(BitArray)

Generate non-empty bit arrays.

Returns

A generator of non-empty bit arrays

Warning

This function will generate bit arrays that cause runtime crashes when targeting JavaScript.

pub fn non_empty_byte_aligned_bit_array() -> Generator(BitArray)

Generate non-empty byte-aligned bit arrays.

pub fn non_empty_string() -> Generator(String)

Generate non-empty strings with the default codepoint and length generators.

Example

use string <- given(string())
string.length(string) > 0
pub fn non_empty_string_from(
  codepoint_generator: Generator(UtfCodepoint),
) -> Generator(String)

Generate non-empty strings with the given codepoint generator and default length generator.

Example

non_empty_string_from(alphanumeric_ascii_codepoint())
pub fn non_empty_utf8_bit_array() -> Generator(BitArray)

Generate non-empty bit arrays of valid UTF-8 bytes.

pub fn option_from(
  generator: Generator(a),
) -> Generator(Option(a))

Create a generator for Option values.

Arguments

  • generator: Generator for the inner value type

Returns

A generator that produces Option values, shrinking towards None first, then towards the shrinks of the input generator

Example

option_from(string())
pub fn parameter(f: fn(a) -> b) -> fn(a) -> b

Support for constructing curried functions for the applicative style of generator composition.

Example

type Box {
  Box(x: Int, y: Int, w: Int, h: Int)
}

fn box_generator() {
  return({
    use x <- parameter
    use y <- parameter
    use w <- parameter
    use h <- parameter
    Box(x:, y:, w:, h:)
  })
  |> apply(bounded_int(-100, 100))
  |> apply(bounded_int(-100, 100))
  |> apply(bounded_int(1, 100))
  |> apply(bounded_int(1, 100))
}
pub fn printable_ascii_codepoint() -> Generator(UtfCodepoint)

Generate printable ASCII characters with a bias towards alphanumeric characters.

Returns

A generator that produces printable ASCII characters as codepoints

Example

string_from(printable_ascii_codepoint())
pub fn random_seed() -> Seed

Create a new randomly-generated seed.

Returns

A Seed value that can be used to configure non-deterministic test generation

Example

let config = config(test_count: 10_000, max_retries: 1, seed: random_seed())
pub fn return(a: a) -> Generator(a)

Create a generator that always returns the same value and does not shrink.

Arguments

  • a: The value to be consistently generated

Returns

A Generator that produces the same input for all test cases

Example

use string <- given(return("Gleam"))
string == "Gleam"
pub fn run(
  config: Config,
  generator: Generator(a),
  property: fn(a) -> Nil,
) -> Nil

Test a property against generated test cases using the provided configuration.

Arguments

  • config: Settings for test execution
  • generator: Creates test inputs
  • property: The property to verify

Returns

  • Nil if all test cases pass (the property returns Nil)
  • Panics if any test case fails (the property panics)
pub fn seed(n: Int) -> Seed

Create a new seed from a provided integer.

Arguments

  • n: Integer to create the seed from

Returns

A Seed value that can be used to configure deterministic test generation

Example

let config = default_config() |> with_seed(seed(124))
pub fn sized_from(
  sized_generator: fn(Int) -> Generator(a),
  size_generator: Generator(Int),
) -> Generator(a)

Creates a generator by first generating a size using the provided size_generator, then passing that size to the sized_generator to produce a value.

Shrinks on the size first, then on the generator.

Arguments

  • sized_generator: A generator function that takes a size and produces a value
  • size_generator: A generator for creating the size input

Returns

A generator that first produces a size, then uses that size to generate a value

Example

Create a bit arrays whose bit size is from 10 to 20.

fixed_size_bit_array() |> sized_from(bounded_int(10, 20))
pub fn small_non_negative_int() -> Generator(Int)

Generate small non-negative integers, well-suited for modeling sized elements like lists or strings.

Shrinks towards 0.

Returns

A generator for small, non-negative integers

Example

generic_string(bounded_codepoint(0, 255), small_non_negative_int())
pub fn small_strictly_positive_int() -> Generator(Int)

Generate small, strictly positive integers, well-suited for modeling sized elements like lists or strings.

Shrinks towards 0.

Returns

A generator for small, strictly positive integers

Example

generic_string(bounded_codepoint(0, 255), small_strictly_positive_int())
pub fn string() -> Generator(String)

Generate strings with the default codepoint and length generators.

Example

use string <- given(string())
string.length(string) == string.length(string <> "!") + 1
pub fn string_from(
  codepoint_generator: Generator(UtfCodepoint),
) -> Generator(String)

Generate strings with the given codepoint generator and default length generator.

Example

string_from(ascii_digit_codepoint())
pub fn then(
  generator: Generator(a),
  f: fn(a) -> Generator(b),
) -> Generator(b)

Transform a generator by applying a function that returns another generator to each generated value.

Unlike map, this allows for a dependency on the resulting generator and the original generated values.

(then is an alias for bind.)

Arguments

  • generator: A generator that creates a value of type a
  • f: A function that takes a value of type a and returns a generator of type b

Returns

A generator that first generates a value of type a, then uses that value to generate a value of type b

Examples

Say you wanted to generate a valid date in string form, like "2025-01-30". In order to generate a valid day, you need both the month (some months have 31 days, other have fewer) and also the year (since the year affects the max days in February). So, before you can generate a valid day, you must first generate a year and a month. You could imagine a set of functions like this:

fn date_generator() -> Generator(String) {
  use #(year, month) <- then(tuple2(year_generator(), month_generator()))
  use day <- map(day_generator(year:, month:))

  int.to_string(year)
  <> "-"
  <> int.to_string(month)
  <> "-"
  <> int.to_string(day)
}

// Note how the day generator depends on the value of `year` and `month`.
fn day_generator(year year, month month) -> Generator(Int) {
  todo
}

fn year_generator() -> Generator(Int) {
  todo
}

fn month_generator() -> Generator(Int) {
  todo
}

Another situation in which you would need then is if you needed to generate departure and arrival times. We will say a pair of departure and arrival times is valid if the departure time is before the arrival time. That means we cannot generate an arrival time without first generating a departure time. Here is how that might look:

fn departure_and_arrival_generator() {
  use departure_time <- then(departure_time_generator())
  use arrival_time <- map(arrival_time_generator(departure_time))
  #(departure_time, arrival_time)
}

fn departure_time_generator() {
  todo
}

fn arrival_time_generator(departure_time) {
  todo
}
pub fn tuple2(
  g1: Generator(a),
  g2: Generator(b),
) -> Generator(#(a, b))

Generate a tuple of two values using the provided generators.

Arguments

  • g1: Generator for the first tuple element
  • g2: Generator for the second tuple element

Returns

A generator that produces a tuple of two values, one from each input generator

Example

let point_generator = tuple2(float(), float())
pub fn tuple3(
  g1: Generator(a),
  g2: Generator(b),
  g3: Generator(c),
) -> Generator(#(a, b, c))

Generate a tuple of three values using the provided generators.

See docs for tuple2.

pub fn tuple4(
  g1: Generator(a),
  g2: Generator(b),
  g3: Generator(c),
  g4: Generator(d),
) -> Generator(#(a, b, c, d))

Generate a tuple of four values using the provided generators.

See docs for tuple2.

pub fn tuple5(
  g1: Generator(a),
  g2: Generator(b),
  g3: Generator(c),
  g4: Generator(d),
  g5: Generator(e),
) -> Generator(#(a, b, c, d, e))

Generate a tuple of five values using the provided generators.

See docs for tuple2.

pub fn tuple6(
  g1: Generator(a),
  g2: Generator(b),
  g3: Generator(c),
  g4: Generator(d),
  g5: Generator(e),
  g6: Generator(f),
) -> Generator(#(a, b, c, d, e, f))

Generate a tuple of six values using the provided generators.

See docs for tuple2.

pub fn uniform_codepoint() -> Generator(UtfCodepoint)

Generate Unicode codepoints.

Returns

A generator that creates Unicode codepoints across the valid range

Notes

  • Generates codepoints from U+0000 to U+10FFFF
  • Uses ASCII lowercase ‘a’ as a shrink target

Example

string_from(uniform_codepoint())
pub fn uniform_int() -> Generator(Int)

Generate uniformly distributed integers across a large range.

Details

  • Shrinks generated values towards 0
  • Not likely to hit interesting or corner cases

Returns

A generator of integers with uniform distribution

Example

let positive_int_generator = {
  use n <- map(uniform_int())
  int.absolute_value(n)
}
pub fn uniform_printable_ascii_codepoint() -> Generator(
  UtfCodepoint,
)

Uniformly generate printable ASCII characters.

Returns

A generator that produces printable ASCII characters as codepoints

Example

string_from(uniform_printable_ascii_codepoint())
pub fn uppercase_ascii_codepoint() -> Generator(UtfCodepoint)

Generate uppercase ASCII letters.

Returns

A generator that produces uppercase letters from A to Z as codepoints

Example

string_from(uppercase_ascii_codepoint())
pub fn utf8_bit_array() -> Generator(BitArray)

Generate bit arrays of valid UTF-8 bytes.

pub fn with_max_retries(
  config: Config,
  max_retries: Int,
) -> Config

Set the maximum number of retries for a property test.

Arguments

  • config: The current configuration
  • max_retries: Maximum number of retries allowed. If max_retries < 0, uses the default max retries.

Returns

A new Config with the specified maximum retries

Example

let config = default_config() |> with_max_retries(100)
pub fn with_seed(config: Config, seed: Seed) -> Config

Set the random seed for reproducible test case generation.

Arguments

  • config: The current configuration
  • seed: Seed value for random number generation

Returns

A new Config with the specified random seed

Example

let config = default_config() |> with_seed(seed(124))
pub fn with_test_count(config: Config, test_count: Int) -> Config

Set the number of test cases to run in a property test.

Arguments

  • config: The current configuration
  • test_count: Number of test cases to generate. If test_count <= 0, uses the default test count.

Returns

A new Config with the specified test count

Example

let config = default_config() |> with_test_count(10_000)
Search Document