Skip to main content

property_test

Attribute Macro property_test 

Source
#[property_test]
Expand description

A variant of #[gpui::test] that supports property-based testing.

A property test, much like a standard GPUI randomized test, allows testing claims of the form “for any possible X, Y should hold”. For example:

#[gpui::property_test]
fn test_arithmetic(x: i32, y: i32) {
    assert!(x == y || x < y || x > y);
}

Standard GPUI randomized tests provide you with an instance of StdRng to generate random data in a controlled manner. Property-based tests have some advantages, however:

  • Shrinking - the harness also understands a notion of the “complexity” of a particular value. This allows it to find the “simplest possible value that causes the test to fail”.
  • Ergonomics/clarity - the property-testing harness will automatically generate values, removing the need to fill the test body with generation logic.
  • Failure persistence - if a failing seed is identified, it is stored in a file, which can be checked in, and future runs will check these cases before future cases.

Property tests work best when all inputs can be generated up-front and kept in a simple data structure. Sometimes, this isn’t possible - for example, if a test needs to make a random decision based on the current state of some structure. In this case, a standard GPUI randomized test may be more suitable.

§Customizing random values

This macro is based on the [#[proptest::property_test]] macro, but handles some of the same GPUI-specific arguments as #[gpui::test]. Specifically, &{mut,} TestAppContext and BackgroundExecutor work as normal. StdRng arguments are explicitly forbidden, since they break shrinking, and are a common footgun.

All other arguments are forwarded to the underlying proptest macro.

Note: much of the following is copied from the proptest docs, specifically the [#[proptest::property_test]] macro docs.

Random values of type T are generated by a Strategy<Value = T> object. Some types have a canonical Strategy - these types also implement Arbitrary. Parameters to a #[gpui::property_test], by default, use a type’s Arbitrary implementation. If you’d like to provide a custom strategy, you can use #[strategy = ...] on the argument:

#[gpui::property_test]
fn int_test(#[strategy = 1..10] x: i32, #[strategy = "[a-zA-Z0-9]{20}"] s: String) {
  assert!(s.len() > (x as usize));
}

For more information on writing custom Strategy and Arbitrary implementations, see [the proptest book][book], and the [Strategy] trait.

§Scheduler

Similar to #[gpui::test], this macro will choose random seeds for the test scheduler. It uses .no_shrink() to tell proptest that all seeds are roughly equivalent in terms of “complexity”. If $SEED is set, it will affect ONLY the seed passed to the scheduler. To control other values, use custom Strategys.

[#[proptest::property_test]]: https://docs.rs/proptest/latest/proptest/attr.property_test.html [book]: https://proptest-rs.github.io/proptest/intro.html [Strategy]: https://docs.rs/proptest/latest/proptest/strategy/trait.Strategy.html