#[property_test]Expand description
A variant of #[gpui::test] that supports property-based testing.
Enable GPUI’s test-support feature in the package’s dev-dependencies before
using this macro. GPUI re-exports the required proptest API, so consumers do
not need to add proptest as a separate direct dependency.
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.
The optional config = ... argument accepts a proptest Config; GPUI applies
its deterministic scheduler seed to that configuration.
Property tests may return proptest’s TestCaseResult, including when using
prop_assert! and related macros:
#[gpui::property_test(
config = gpui::proptest::test_runner::Config::with_cases(4),
)]
fn test_result(x: i32) -> gpui::proptest::test_runner::TestCaseResult {
gpui::proptest::prop_assert_eq!(x, x);
Ok(())
}Note: much of the following is copied from 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
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.