#[define]Expand description
Populates a module tree with test cases parameterizing generic definitions.
This macro is used to annotate a module containing test case definitions. All functions defined directly in the module and marked with a test attribute must have the same number and order of generic type parameters.
Empty submodules defined inline at any depth under the module on which
the macro is invoked can be annotated with the instantiate_tests
attribute. The macro populates these submodules with functions having names,
signatures, and test attributes mirroring the generic test functions at the
macro invocation’s root module. Each of the instantiated functions calls
its generic namesake in the root module, parameterized with the arguments
given in instantiate_tests.
§Basic example
#[generic_tests::define]
mod tests {
use std::borrow::Cow;
use std::fmt::Display;
#[test]
fn print<S>()
where
S: From<&'static str> + Display,
{
let s = S::from("Hello, world!");
println!("{}", s);
}
#[instantiate_tests(<String>)]
mod string {}
#[instantiate_tests(<&'static str>)]
mod str_slice {}
#[instantiate_tests(<Cow<'static, str>>)]
mod cow {}
}§Test attributes
The macro checks attributes of the function items directly contained
by the module against a customizable set of attribute paths that annotate
test cases. Functions with at least one of the attributes found in this set
are selected for instantiation.
These attributes are replicated to the instantiated test case functions and
erased from the original generic definitions. By default, the
test, bench, ignore, and should_panic attributes get this
treatment. To recognize other test attributes, their paths can be
listed in the attrs() parameter of the define attribute. Use of the
attrs() parameter overrides the default set.
#[generic_tests::define(attrs(tokio::test))]
mod async_tests {
use bytes::{Buf, Bytes};
use tokio::io::{self, AsyncWriteExt};
#[tokio::test]
async fn test_write_buf<T: Buf>() -> io::Result<()>
where
T: From<&'static str>,
{
let mut buf = T::from("Hello, world!");
io::sink().write_buf(&mut buf).await?;
Ok(())
}
#[instantiate_tests(<Vec<u8>>)]
mod test_vec {}
#[instantiate_tests(<Bytes>)]
mod test_bytes {}
}The copy_attrs() list parameter can be used to specify item attributes
that are both copied to the instantiated test case functions and preserved
on the generic functions. By default, this set consists of cfg,
enabling consistent conditional compilation.
#[generic_tests::define(copy_attrs(cfg, cfg_attr))]
mod tests {
use super::Foo;
#[test]
#[cfg(windows)]
fn test_only_on_windows<T>() {
// ...
}
#[test]
#[cfg_attr(feature = "my-fn-enhancer", bells_and_whistles)]
fn test_with_optional_bells_and_whistles<T>() {
// ...
}
#[instantiate_tests(<Foo>)]
mod foo {}
}The attribute sets can be customized for an individual generic test
function with the generic_test attribute.
#[generic_tests::define]
mod tests {
use super::Foo;
#[generic_test(attrs(test, cfg_attr), copy_attrs(allow))]
#[test]
#[cfg_attr(windows, ignore)]
#[allow(dead_code)]
fn works_everywhere_except_windows<T>() {
// ...
}
#[instantiate_tests(<Foo>)]
mod foo {}
}Finally, all function parameter attributes on the generic test functions are always copied into the signatures of the instantiated functions.
§Const generics
Since Rust 1.51, const generic parameters can be used to parameterize test cases in addition to type parameters.
#[generic_tests::define]
mod tests {
use std::iter;
#[test]
fn test_fill_vec<const LEN: usize>() {
let _v: Vec<u8> = iter::repeat(0xA5).take(LEN).collect();
}
#[instantiate_tests(<16>)]
mod small {}
#[instantiate_tests(<65536>)]
mod large {}
}