test-strategy
This crate provides two procedural macros, #[derive(Arbitrary)] and #[proptest].
Each of these macros is an alternative to the following proptest's official macros.
| test-strategy | proptest | proptest-derive |
|---|---|---|
#[derive(Arbitrary)] |
#[derive(Arbitrary)] |
|
#[proptest] |
proptest ! { } |
The macros provided by this crate have the following advantages over the proptest's official macros.
- Supports higher-order strategies. (
#[derive(Arbitrary)]and#[proptest]) - Code formatting is not disabled. (
#[proptest])
However, the syntax of this crate's macros are not compatible with the syntax of the official macros.
Install
Add this to your Cargo.toml:
[]
= "0.4.3"
= "1.6.0"
Example
You can use #[derive(Arbitrary)] to automatically implement proptest's Arbitrary trait.
use Arbitrary;
You can define a property test by adding #[proptest] to the function.
use proptest;
Attributes
Attributes can be written in the following positions.
| attribute | function | struct | enum | variant | field | function parameter |
|---|---|---|---|---|---|---|
#[strategy] |
✔ | ✔ | ||||
#[any] |
✔ | ✔ | ||||
#[weight] |
✔ | |||||
#[map] |
✔ | ✔ | ||||
#[filter] |
✔ | ✔ | ✔ | ✔ | ✔ | ✔ |
#[by_ref] |
✔ | ✔ | ||||
#[arbitrary(args = T)] |
✔ | ✔ | ||||
#[arbitrary(bound(...))] |
✔ | ✔ | ✔ | ✔ | ||
#[arbitrary(dump)] |
✔ | ✔ | ||||
#[proptest] |
✔ | |||||
#[proptest(async = ...)] |
✔ | |||||
#[proptest(dump)] |
✔ |
#[derive(Arbitrary)]
You can implement proptest::arbitrary::Arbitrary automatically by adding #[derive(Arbitrary)] to struct or enum declaration.
By default, all fields are set using the strategy obtained by proptest::arbitrary::any().
So the following two codes are equivalent.
use Arbitrary;
use ;
#[strategy]
You can specify a strategy to generate values for the field by adding #[strategy(...)] to the field.
In the following example, the value of field x will be less than 20.
use Arbitrary;
In #[strategy], the values of other fields can be used by following # to the name of the field.
In the following example, the value of y is less than or equal to x.
use Arbitrary;
#[any]
Instead of writing #[strategy(any_with::<Type>(expr))], you can write #[any(expr)].
use size_range;
use Arbitrary;
Instead of writing an expression to be passed to any_with, you can write only the value of the field to be changed from the default value.
Therefore, the following TestInputA, TestInputB and TestInputC are equivalent.
use Arbitrary;
#[weight]
By default, all variants appear with equal probability.
You can add #[weight] to the variant to change the probability of the variant appearing.
In the following example, TestInput::B is twice as likely to appear as TestInput::A.
use Arbitrary;
If you add #[weight(0)] to a variant, the variant does not appear, so you can use a type in that variant that cannot be used as Arbitrary.
use Arbitrary;
;
#[map]
Instead of using prop_map in #[strategy(...)], #[map(...)] can be used.
The following codes mean the same thing.
use any;
use Strategy;
use Arbitrary;
References to other fields in the function applied to prop_map or #[map(...)] will generate different strategies.
Referencing another field in #[strategy(...)] will expand it to prop_flat_map, even if it is in prop_map.
use any;
use ;
use Arbitrary;
// The code above generates the following strategy.
let t1 =
.prop_flat_map
.prop_map;
On the other hand, if you refer to another field in #[map], it will expand to prop_map.
use any;
use Strategy;
use Arbitrary;
// The code above generates the following strategy.
let t2 = .prop_map;
If the input and output types of the function specified in #[map] are different, the value type of the strategy set in #[strategy] is the type of the function's input, not the type of the field.
use any;
use Index;
use Arbitrary;
// `#[strategy(any::<Index>())]` can be omitted.
#[filter]
By adding #[filter] , you can limit the values generated.
In the following examples, x is an even number.
use Arbitrary;
You can also use multiple variables in a predicate.
use Arbitrary;
You can use the value of a structure or enum in the filter by using #self.
use Arbitrary;
If the expression specified for #[filter] does not contain a variable named by appending # to its own field name, the expression is treated as a predicate function, rather than an expression that returns a bool.
use Arbitrary;
Similarly, an expression that does not contain #self in the #[filter(...)] that it attaches to a type is treated as a predicate function.
use Arbitrary;
You can specify a filter name by passing two arguments to #[filter].
use Arbitrary;
#[by_ref]
By default, if you use a variable with #[strategy], #[any], #[map] or #[filter] with # attached to it, the cloned value is set.
Adding #[by_ref] to the field makes it use the reference instead of the cloned value.
use Arbitrary;
#[arbitrary]
#[arbitrary(args = T)]
Specifies the type of Arbitrary::Parameters.
You can use the Rc value of this type in #[strategy], #[any], or #[filter] with the variable name args.
use Arbitrary;
#[arbitrary(bound(T1, T2, ..))]
By default, if the type of field for which #[strategy] is not specified contains a generic parameter, that type is set to trait bounds.
Therefore, the following TestInputA and TestInputB are equivalent.
use ;
use Arbitrary;
Types of fields with #[strategy] do not set trait bounds automatically, so you need to set trait bound manually with #[arbitrary(bound(T))].
use any_with;
use Arbitrary;
You can also specify where predicate instead of type.
use ;
use Arbitrary;
.. means automatically generated trait bounds.
The following example uses a manually specified trait bounds in addition to the automatically generated trait bounds.
use any_with;
use Arbitrary;
#[arbitrary(dump)]
Causes a compile error and outputs the code generated by #[derive(Arbitrary)] as an error message.
#[proptest]
#[proptest] is the attribute used instead of #[test] when defining a property test.
The following example defines a test that takes a variety of integers as input.
use proptest;
You can add #[strategy], #[any], #[filter], #[by_ref] to the parameter of the function with # [proptest].
use proptest;
You can change the configuration of a property test by setting the argument of #[proptest] attribute to a value of proptest::prelude::ProptestConfig type.
use ProptestConfig;
use proptest;
As with #[any], you can also set only the value of the field to be changed from the default value.
The example below is equivalent to the one above.
use ProptestConfig;
use proptest;
#[proptest(async = ...)]
Async functions can be tested by setting async = ... to the argument of #[proptest].
The following values are allowed after async =.
The value specifies the asynchronous runtime used for the test.
- "tokio"
[]
= "0.4.3"
= "1.6.0"
= { = "1.38.0", = ["rt-multi-thread"] }
use proptest;
use prop_assert;
async
#[proptest(dump)]
You can use #[proptest(dump)] and output the code generated by #[proptest] as an compile error message.
#[proptest(dump)]
fn my_test(_input: i32) {
// ...
}
License
This project is dual licensed under Apache-2.0/MIT. See the two LICENSE-* files for details.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.