spoke::test!
Human readable test case writing for Rust
[!WARNING]
This library is not yet production-ready.Feedback and suggestions welcomed
Spoke::test! is a proc-macro for the Rust programming language to reduce the time and effort involved in writing tests.
The macro transforms the simplified syntax into standard Rust #[test] functions but saves significant typing.
Features
for planned features see TODO
Getting Started
Add the crate as a dependency
Then in your Rust code (e.g. main.rs ) you can add test cases inside a call to spoke::test!, e.g.
test!
The Principle
Spoke::test! resolves around the $ symbol and what follows it to define nested sequential tests and assertions.
Test names are introduced as strings using $"" which enables requirements capture without trying to introduce underscores between each word.
Nested tests concatenate the names to produce unique test names. Spoke::test! converts these human-readable strings into function names.
You can imagine nested tests creating a sort of tree structure, each leaf of the tree becomes a unique test function (leaves are normally assertions).
Each test name is followed by either a body {} or an assertion which is ended with a ;.
Within a body any code not being preceded by a $ is included in that test (and in any nested tests).
Sequential Tests
Nesting requirement bodies allows for creation of sequential tests - that is, multiple tests that used the same setup but validate different assertions.
test!
// becomes
Preamble
Sometimes it is necessary to introduce use statements to pull in other crates, this can be done inside the spoke::test! call and is generated as an internal preamble at the start of the test module
test!
// becomes
Assertions
assert
The simplest assert is written as a named requirement followed by a boolean expression.
$"requirement" <expression>;
which maps to a standard Rust assertion like
assert!(<expression>);
and the requirement is folded into the test name.
Simple assertion example
$"value should be square" is_square;
// becomes
assert_eq and assert_ne
Rusts equality assertions are also supported using an infix notation $eq and $ne.
$"requirement" <expression_1> $eq <expression_2> ;
which maps to a standard Rust assertion like
assert_eq!( <expression_1> , <expression_2> );
and the requirement is folded into the test name.
Equality assertion example
$"multiplication"
// becomes
Dropping spoke::test!
So you tried spoke::test! and decided you don't like it? No problem.
Open your Rust file in vscode, navigate to the call to spoke::test! and then right-click, "refactor", "inline macro". The call to spoke::test! will be replaced with the generated tests and you can then remove spoke from your cargo.toml and carry on as if you had never used it.
Is this a full replacement for Rusts #[test] ?
Definitely not. spoke::test! is helpful in some scenarios but you should pick the testing methodology that best captures the requirements. Feel free to mix and match some tests with spoke::test! and some the standard way.
Optional Features
There are currently no optional features.
Known Issues
Due to limitations of the proc-macro (and proc-macro2) libraries on stable some of the compile errors are highlighted against a single token when they realistically apply to multiple tokens. Improvements can be made here when the proc_macro_span feature stabilises.
If any test in a spoke requires the variable to be mutable it must be mutable for all. This can sometimes cause issues. The workaround is to split up the branches which can mean undesired duplication. Until such time as we get proper reflection I don't have a perfect solution for this (ideally the test would just drop unused mutability based on compiler feedback).
Missing features (planned)
Currently the following features of standard Rust tests are planned but as yet unavailable.
- Ignoring tests
- Expected panics
- Changing the module name
- Custom configurations
Feedback
If you have thoughts, suggestions, or concerns please feel free to create a Discussion or directly raise an Issue.
License
This project uses the MIT license and that license shall automatically apply to any contributions made.