#property-testing #properties #quickcheck #fuzz #hypothesis

checkito

A simple quickcheck inspired library to generate growable/shrinkable random data mainly oriented towards generative/property/exploratory testing

27 releases (stable)

1.5.1 Mar 9, 2024
1.4.2 Feb 19, 2024
1.3.9 Dec 27, 2023
1.3.8 Aug 21, 2023
0.1.4 Feb 27, 2023

#81 in Testing

31 downloads per month
Used in scalp

MIT license

115KB
3K SLoC

checkito

A simple QuickCheck inspired library to generate shrinkable random data mainly oriented towards generative/property/exploratory testing. One would use this library to prove that certain properties hold for a program for a tentatively representative sample of their input space.


  • The Generate trait that is implemented for many of rust's standard types allows the generation of any random composite data through combinator (such as tuples, Any, Map, Flatten and more). It is designed for composability and its usage should feel like working with Iterators.
  • The Shrink trait tries to reduce a generated sample to a 'smaller' version of it while maintaining its constraints (ex: a sample usize in the range 10..100 will never be shrunk out of its range). For numbers, it means bringing the sample closer to 0, for vectors, it means removing irrelevant items and shrinking the remaining ones, etc..
  • The Prove trait is meant to represent a desirable property of a system under test. It is used mainly in the context of the Generate::check or Checker::check methods and it is the failure of a proof that triggers the shrinking process. It is implemented for a couple of standard types such as bool and Result.

Example

use checkito::{check::Error, *};

struct Composite(String, f64);

fn main() {
    // Parse this pattern as a [`Regex`] which implements the [`Generate`] trait. The '_' character is included in the regex
    // to make the checks below fail (for illustration purposes).
    let regex = regex!("[a-zA-Z0-9_]*");
    // [`f64`] ranges implement the [`Generate`] trait.
    let number = 10.0f64..;
    // Combine the previous [`Generate`] implementations and map them to a custom `struct`.
    let composite = (regex, number).map(|pair| Composite(pair.0, pair.1));

    // Generate 1000 [`Composite`] values which are checked to be alphanumeric.
    // [`Generate::check`] will fail when a '_' will appear in `value.0` and the shrinking process will begin.
    let result: Result<_, _> = composite.check(1000, |value: &Composite| {
        value.0.chars().all(|character| character.is_alphanumeric())
    });
    // `result` will be [`Err`] and will hold the original and shrunk values.
    let error: Error<Composite, _> = result.unwrap_err();
    let _original: &Composite = &error.original;
    // The expected shrunk value is [`Composite("_", 10.0)`].
    let _shrunk: &Option<Composite> = &error.shrunk;

    // Alternatively, generated samples can be retrieved directly, bypassing shrinking.
    for value in composite.samples(1000) {
        // This assertion is almost guaranteed to fail because of '_'.
        assert!(value.0.chars().all(|character| character.is_alphanumeric()));
    }
}

See the examples and tests folder for more detailed examples.

Alternatives

Dependencies

~1–3MB
~67K SLoC