quickcheck

repository·master·Indexed 25 days ago

https://github.com/burntsushi/quickcheck

A property-based testing framework for Rust (version 1.1.0) that uses randomly generated inputs to verify properties. It features automatic shrinking to find minimal counter-examples, the Arbitrary trait for custom type generation, and integration with Rust's test runner via the quickcheck! macro and #[quickcheck] attribute.

Tokens
3.7K
Snippets
11
Records
26
Agent score
82%

What's inside quickcheck

  1. Install quickcheck

    master

    Add quickcheck to your Cargo.toml dependencies. If you only need it for testing, add it to [dev-dependencies] instead.

    To use the #[quickcheck] attribute, you must also include quickcheck_macros in your [dev-dependencies].

    # For production use
    [dependencies]
    quickcheck = "1"
    
    # For testing only
    [dev-dependencies]
    quickcheck = "1"
    quickcheck_macros = "1"
  2. Increase testing thoroughness

    master

    Quickcheck uses random input, so you can improve the likelihood of finding bugs by increasing the number of tests performed. You can do this in two ways:

    1. Crank the number of tests: Use the .tests() method on a QuickCheck instance or set the QUICKCHECK_TESTS environment variable. This runs a bounded number of tests, making it suitable for release cycles.
    2. Run in a loop: Use a shell script to run cargo test repeatedly until a failure is detected. This is useful for continuous testing.

    If you require continuous, unbounded fuzzing, consider using cargo fuzz.

  3. Configure test counts via environment variables

    master

    You can control the number of tests performed and the threshold for success using the following environment variables:

    • QUICKCHECK_TESTS: The number of tests to run (default is 100).
    • QUICKCHECK_MAX_TESTS: The maximum number of attempts to find valid tests before giving up (default is 10,000).
    • QUICKCHECK_MIN_TESTS_PASSED: The minimum number of valid tests that must pass for the suite to be considered a success (default is 0).
  4. Run Quickcheck properties in a loop via Bash

    master

    To run your Quickcheck properties continuously until a failure occurs, you can use a bash script. It is recommended to prefix your Quickcheck test functions with qc_ so you can filter them using cargo test qc_ to avoid running all deterministic tests in every iteration.

    #!/usr/bin/bash
    
    while true
    do
        cargo test qc_
        if [[ x$? != x0 ]] ; then
            exit $?
        fi
    done
  5. Use the quickcheck! macro

    master

    The quickcheck! macro allows you to define a property function that is automatically run with randomly generated inputs. This macro is backwards compatible with older versions of Rust.

    #[cfg(test)]
    mod tests {
        use quickcheck::quickcheck;
        use super::reverse;
    
        quickcheck! {
            fn prop(xs: Vec<u32>) -> bool {
                xs == reverse(&reverse(&xs))
            }
        }
    }
  6. Run a Quickcheck property

    master

    To execute a property test, pass the function pointer of your property to the quickcheck function. The property function should take the input types and return a bool (where true indicates the property holds and false indicates a failure).

    fn prop_all_prime(n: usize) -> bool {
        sieve(n).into_iter().all(is_prime)
    }
    
    fn main() {
        quickcheck(prop_all_prime as fn(usize) -> bool);
    }
  7. Generate random structs by implementing Arbitrary

    master

    To generate random instances of a custom struct, you must implement the Arbitrary trait for that struct. Inside the arbitrary method, use the provided Gen instance to generate the individual fields of the struct.

    use quickcheck::{Arbitrary, Gen};
    
    struct Point {
        x: i32,
        y: i32,
    }
    
    impl Arbitrary for Point {
        fn arbitrary(g: &mut Gen) -> Point {
            Point {
                x: i32::arbitrary(g),
                y: i32::arbitrary(g),
            }
        }
    }
  8. Use the #[quickcheck] attribute

    master

    The #[quickcheck] attribute (provided by the quickcheck_macros crate) converts a property function directly into a standard Rust #[test] function, making it easier to integrate with the default test runner.

    #[cfg(test)]
    mod tests {
        use quickcheck_macros::quickcheck;
        use super::reverse;
    
        #[quickcheck]
        fn double_reversal_is_identity(xs: Vec<isize>) -> bool {
            xs == reverse(&reverse(&xs))
        }
    }
  9. Discard test results using TestResult

    master

    If a property only holds for a specific subset of inputs, you can discard inputs that fall outside that subset by returning a TestResult instead of a bool. This prevents the test from failing or passing on invalid inputs.

    To do this, your property function must return TestResult. You can use TestResult::discard() to skip an input or TestResult::from_bool(bool) to convert a boolean result.

  10. Configure QuickCheck via environment variables

    master

    You can control the default behavior of QuickCheck::new() using the following environment variables:

    VariableDefaultDescription
    QUICKCHECK_TESTS100The target number of passed tests.
    QUICKCHECK_MAX_TESTS10000The absolute maximum number of property invocations.
    QUICKCHECK_GENERATOR_SIZE100The initial size for the random number generator.
    QUICKCHECK_MIN_TESTS_PASSED0The minimum number of valid passed tests required for success.
  11. Known limitations of Quickcheck

    master

    When using this port of QuickCheck, be aware of the following limitations:

    • Parameter Limit: Only functions with 8 or fewer parameters can be quickchecked.
    • Stack Overflows: Failures caused by stack overflows are not caught and will not have a witness (counter-example) attached.
    • Missing Traits: Coarbitrary is not implemented.
    • Closures: Arbitrary is not implemented for closures.