test_case
crate provides procedural macro attribute that generates parametrized test instances.
Crate has to be added as a dependency to Cargo.toml
:
toml
[dev-dependencies]
test-case = "2.0.0-rc1"
and imported to the scope of a block where it's being called
(since attribute name collides with rust's built-in custom_test_frameworks
) via:
rust
use test_case::test_case;
```rust
mod tests { use testcase::testcase;
#[test_case(-2, -4 ; "when both operands are negative")]
#[test_case(2, 4 ; "when both operands are positive")]
#[test_case(4, 2 ; "when operands are swapped")]
fn multiplication_tests(x: i8, y: i8) {
let actual = (x * y).abs();
assert_eq!(8, actual)
}
} ```
Output from cargo test
for this example:
```sh $ cargo test
running 4 tests test tests::multiplicationtests::whenbothoperandsarenegative ... ok test tests::multiplicationtests::whenbothoperandsarepositive ... ok test tests::multiplicationtests::whenoperandsareswapped ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out ```
For #[test_case(body)]
the body is built as follows:
body
:= $arguments ($expected_result)? ($description)?
arguments
:= $expr(,$expr)*(,)?
Comma separated list of one or more expressions, eg.: ```rust
```
expected_result
:= => ($modifier)* $validator
Optional part that provides assertions to instantiated tests.
When using expected_result
version of test_case
tested function must return a type
that can be matched with validator. Each validator description states how to ensure
that the type returned by function can be matched.
modifier
:= ignore | inconclusive
Both ignore
and inconclusive
keywords indicate that test case should be skipped. This is equivalent to using
#[ignore]
attribute on normal test. Eg.:
```rust
```
There are numerous validators provided by test_case
:
validator
:= $simple|$matching|$panicking|$with|$using|$complex
simple
:= $expr
Accepts any expression that evaluates to function return type and
compares it against whatever tested block returns via assert_eq
. Eg.:
```rust
fn parsesastring(arg_in: &str) -> f64 { body omitted... } ```
matching
:= matches $pattern
A pattern following keyword matches
.
Result of a function is compared to pattern
via MatchExpression. Eg.:
```rust
```
panicking
:= panics ($expr)?
Indicates that test instance should panic. Works identical to #[should_panic]
test attribute.
Optional expression after the keyword is treated like expected
in should_panic. Eg.:
```rust
```
with
:= with $closure
Allows manual assertions of the result of testing function. Closure must indicate argument type and it has to be implicitly convertible from type returned by testing function. Eg.:
```rust
fn test_division(i: f64) -> f64 { 0.0 / i } ```
using
:= using $path
Work similar to with
attribute, with the difference being that instead of a closure
it accepts path to a function that should validate result of the testing function. Eg.:
```rust fn ispoweroftwo(input: u64) { assert!(input.ispoweroftwo()) }
fn some_test(input: u64) -> u64 { "body omitted..." } ```
complex
:= (it|is) $complex_expression
complex_expression
:= not $complex_expression_inner | $complex_expression_inner (and $complex_expression_inner)* | $complex_expression_inner (or $complex_expression_inner)*
complex_expression_inner
:= $cmp_assertion|$path_assertion|$collection_assertion|\($complex_expression\)
cmp_assertion
:= $ord_assertion|$almost_eq_assertion
path_assertion
:= existing_path|file|dir|directory
collection_assertion
:= contains $expr|contains_in_order $expr
ord_assertion
:= (eq|equal_to|lt|less_than|gt|greater_than|leq|less_or_equal_than|geq|greater_or_equal_than) $expr
almost_eq_assertion
:= (almost_equal_to|almost) $expr precision $expr
Complex assertions are created as an extension to test_case
allowing for more flexibility in comparisons. Eg.:
```rust
fn takepieceof_circle(...) -> f64 { "body omitted..." }
fn installationcreatedpath(...) -> PathBuf { "body omitted..." }
fn somevolatilecomputation(...) -> f64 { "body omitted..." }
fn listofusers(...) -> Vec
fn sorts_asc(...) -> Vec
it
and is
have equivalent interpretation. Both variants exist in order to make test cases easier to read.
complex assertions are WIP content, use at own discretion.
If test_case
is used with async
tests, eg. #[tokio::test]
, or user wants to pass other attributes to each
test instance then additional attributes have to be added past first occurrence of #[test_case]
. Eg.:
```rust
async fn xyz() { } ```
Licensed under of MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)
Project roadmap is available at link. All contributions are welcome.
Recommended tools:
* cargo readme
- to regenerate README.md based on template and lib.rs comments
* cargo insta
- to review test snapshots
* cargo edit
- to add/remove dependencies
* cargo fmt
- to format code
* cargo clippy
- for all insights and tips
* cargo fix
- for fixing warnings