# parameterized
Procedural macro based parameterized testing library.
Useful, when you want to run a test case with many different input sets.
When defining a parameterized test case, the `#[parameterized(...)]` attribute should be used instead of `#[test]`.
This crate was inspired by JUnit `@ParameterizedTest`.
If you consider using parameterized, you can also check out [Yare](https://github.com/foresterre/yare) which is a
variation on `parameterized`, which pivots the parameters, so you can define your own identifier for cases.
Alternatively, there is [Sif](https://github.com/foresterre/sif) where each case can be defined by a
separate `#[case(...)`] attribute.
### Example:
Additional examples can be found at the <a href="https://github.com/foresterre/parameterized-examples">
parameterized-examples repository</a>,
and in the <a href="parameterized-macro/tests">tests</a> folder.
```rust
enum Fruit {
Apple,
Bramble(BrambleFruit),
Pear,
}
trait NameOf {
fn name_of(&self) -> &str;
}
impl NameOf for Fruit {
fn name_of(&self) -> &str {
match self {
Fruit::Apple => "apple",
Fruit::Bramble(fruit) => fruit.name_of(),
Fruit::Pear => "pear",
}
}
}
enum BrambleFruit {
Blackberry,
}
impl NameOf for BrambleFruit {
fn name_of(&self) -> &str {
match self {
BrambleFruit::Blackberry => "blackberry",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use parameterized::parameterized;
#[parameterized(fruit = {
Fruit::Apple, Fruit::Pear, Fruit::Bramble(BrambleFruit::Blackberry)
}, name = {
"apple", "pear", "blackberry"
})]
fn a_fruity_test(fruit: Fruit, name: &str) {
assert_eq!(fruit.name_of(), name)
}
}
```
### Custom test attributes (e.g. tokio::test)
By default, the code generation step of the `parameterized` attribute will generate test cases marked with a `#[test]`
attribute.
For example, for the parameterized test case **add5** below, the following code would be generated:
**Parameterized test case**
```rust
use parameterized::parameterized;
#[parameterized(input = {
0, 1
}, expected = {
5, 6
})]
fn add5(input: u32, expected: u32) {
assert_eq!(input + 5, expected);
}
```
**Generated code**
```rust
#[cfg(test)]
mod add5 {
use super::*;
#[test]
fn case_0() {
assert_eq!(0 + 5, 5);
}
#[test]
fn case_1() {
assert_eq!(1 + 5, 6);
}
}
```
However, sometimes a different test macro is desired, for example with `#[tokio::test]`.
To let `#[parameterized]` use a user specified test macro, you may add the `#[parameterized_macro(...)]` attribute after
a `#[parameterized]` attribute.
Since we use `#[tokio::test]` in this example, we also add the `async` item to the function signature (but this is of
course not mandatory for other macros).
**Parameterized test case with `#[parameterized_macro(...)]`**
```rust,ignore
#[parameterized(input = {
0, 1
}, expected = {
5, 6
})]
#[parameterized_macro(tokio::test)]
async fn add5(input: u32, expected: u32) {
assert_eq!(input + 5, expected);
}
```
Gotchas:
* The `#[parameterized_macro(...)]` must always be specified after a `#[parameterized(...)]` attribute
* For now, only one `#[parameterized_macro(...)]` attribute per parameterized test function is supported.
* While you can rename the parameterized attribute using import renaming (
e.g. `use parameterized::parameterized as pm`),
the `parameterized_macro` attribute cannot be renamed, since it's not actually defined as a separate macro.
Instead, the `parameterized` parses this attribute as well.
### Imports
If you prefer not to import this library (with `use parameterized::parameterized;`) in every test module, you can put
the following snippet at the top of your crate root:
```rust
#[cfg(test)]
#[macro_use]
extern crate parameterized;
```
<br>
### IDE 'run test' intent
IntelliJ IDEA recognizes test cases and provides context menus which allow you to run tests within a certain scope
(such as a module or a single test case). For example, in IntelliJ you can usually run individual test cases by clicking
the ▶ icon in the gutter. Unfortunately, attribute macros are currently not expanded by `intellij-rust`.
This means that the IDE will not recognize test cases generated as a result of attribute macros (such as the
`parameterized` macro published by this crate).
A workaround can be found below (if you have a better solution, please feel free to open an issue; thank you in
advance!)
```rust
fn squared(input: i8) -> i8 {
input * input
}
#[cfg(test)]
mod tests {
use super::*;
use parameterized::parameterized as pm;
use parameterized::ide;
mod squared_tests { // <--
use super::*;
ide!(); // <--
#[pm(input = {
-2, -1, 0, 1, 2
}, expected = {
4, 1, 0, 1, 4
})]
fn test_squared(input: i8, output: i8) {
assert_eq(squared(input), output);
}
}
}
```
Here we created an empty test case (using the `ide!()` macro) which will mark the surrounding module as 'containing test
cases'. In
the gutter you will find the ▶ icon next to the module. This allows you to run test cases per module.
Note: `intellij-rust` does expand declarative macro's (with the new macro engine which can be
selected in the 'settings' menu), such as this `ide!` macro.
<br>
### License
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
<sub>
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.</sub>