I want to write test cases that depend on parameters. My test case should be executed for each parameter and I want to see whether it succeeds or fails for each parameter.
I'm used to writing things like that in Java:
@RunWith(Parameterized.class)
public class FibonacciTest {
@Parameters
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 }
});
}
private int fInput;
private int fExpected;
public FibonacciTest(int input, int expected) {
fInput= input;
fExpected= expected;
}
@Test
public void test() {
assertEquals(fExpected, Fibonacci.compute(fInput));
}
}
How can I achieve something similar with Rust? Simple test cases are working fine, but there are cases where they are not enough.
#[test]
fn it_works() {
assert!(true);
}
Note: I want the parameters as flexible as possible, for example: Read them from a file, or use all files from a certain directory as input, etc. So a hardcoded macro might not be enough.
Probably not quite what you've asked for, but by using
TestResult::discard
with quickcheck you can test a function with a subset of a randomly generated input.I took the Fibonacci test from this Quickcheck tutorial.
P.S. And of course, even without macros and quickcheck you still can include the parameters in the test. "Keep it simple".
It's possible to construct tests based on arbitrarily complex parameters and any information known at build time (including anything you can load from a file) with a build script.
We tell Cargo where the build script is:
Cargo.toml
In the build script, we generate our test logic and place it in a file using the environment variable
OUT_DIR
:build.rs
Finally, we create a file in our tests directory that includes the code of the generated file.
tests/generated_test.rs
That's it. Let's verify that the tests are run:
My
rstest
crate mimicspytest
syntax and provides a lot of flexibility. A Fibonacci example can be very neat:Output:
Every case is run as a single test case.
The syntax is simple and neat and, if you need, you can use any Rust expression as the value in the
case
argument.rstest
also supports generics andpytest
-like fixtures.Don't forget to add
rstest
todev-dependencies
inCargo.toml
.Building off Chris Morgan’s answer, here's a recursive macro to create parameterized tests (playground):
You can use it like so:
parameterized_test!
defines a new macro (even!
) that will create parameterized tests taking one argument (n
) and invokingassert_eq!(n % 2, 0);
.even!
then works essentially like Chris'fib_tests!
, though it groups the tests into a module so they can share a prefix (suggested here). This example results in two tests functions,even::one
andeven::two
.This same syntax works for multiple parameters:
The
with_dollar_sign!
macro used above to essentially escape the dollar-signs in the inner macro comes from @durka:I've not written many Rust macros before, so feedback and suggestions are very welcome.
Use https://github.com/frondeus/test-case crate.
Example:
The built-in test framework does not support this; the most common approach used is to generate a test for each case using macros, like this:
This produces individual tests with names
fib_0
,fib_1
, &c.