Reputation: 6362
Let's say I have a Rust function named add
that adds two numbers. I want to write a comprehensive set of unit tests to ensure it always works.
From what I know, there are two ways you can write the tests.
assert_eq!
multiple times in one test functionNeither of these are ideal (see below). Is there another, better way to write unit tests in Rust?
#[test]
fn test() {
assert_eq!(add(1, 2), 3);
assert_eq!(add(2, 4), 5);
assert_eq!(add(2, 3), 5);
}
This works well when everything passes. But if one assertion fails, you don't know which one. For instance, in the above test, the only help you get is "left: 6, right: 5". Which assertion caused this? It's fairly easy to tell because add
is trivial, but in anything non-trivial, you have no idea.
#[test]
fn add_1_and_2() {
assert_eq!(add(1, 2), 3);
}
#[test]
fn add_2_and_4() {
assert_eq!(add(2, 4), 6);
}
This solves all the problems from using multiple assertions. However, the tests are becoming extremely verbose, and you need to create a unique function name for every single test. Imagine a function with 4-5 different parameters and you're testing all combinations. That's a lot of hard-to-name functions! But at least you'll know which assertion breaks.
Ideally, I'm looking for a solution with the same functionality as this JavaScript test using Jest.
const tests = [[1, 2, 3], [2, 4, 6]];
it.each(tests)("adding %s and %s gives %s", ([a, b, result]) => {
expect(add(a, b)).toBe(result);
})
If one of the tests fail, Jest will tell you exactly which one. And the actual test code is very concise and readable.
Upvotes: 5
Views: 2031
Reputation: 76459
Part of this is a style issue and personal preference. The first approach is very common when performing many similar operations, and so I'd say it's usual.
However, that doesn't mean you can't tell which test has failed. I get output like this:
thread 'tests::test' panicked at 'assertion failed: `(left == right)`
left: `6`,
right: `5`', src/main.rs:15:9
That tells me that the assertion on line 15 failed. If you're calling into some function that does testing and calls assert_eq!
and you need to know which of those calls caused it, you can use RUST_BACKTRACE=1
to see the backtrace and find the appropriate line.
You can also write a description (with additional formatting arguments, just like format!
) after the description:
#[test]
fn test() {
assert_eq!(add(1, 2), 3, "add 1 and 2");
assert_eq!(add(2, 4), 5, "add 2 and 4");
assert_eq!(add(2, 3), 5, "add 2 and 3");
}
Then you'll see the test name that fails in the output (notice it now says "add 2 and 4"):
thread 'tests::test' panicked at 'assertion failed: `(left == right)`
left: `6`,
right: `5`: add 2 and 4', src/main.rs:15:9
And if you prefer a table-driven approach, there are ways to do that as well:
#[test]
fn test() {
let conditions = &[(1, 2, 3), (2, 4, 5), (2, 3, 5)];
for (a1, a2, sum) in conditions {
assert_eq!(add(*a1, *a2), *sum, "add {} and {}", a1, a2);
}
}
Or, if your test arguments are long and not easily printable:
#[test]
fn test() {
let conditions = &[
(1, 2, 3, "test case 1"),
(2, 4, 5, "test case 2"),
(2, 3, 5, "test case for bug #12345"),
];
for (a1, a2, sum, desc) in conditions {
assert_eq!(add(*a1, *a2), *sum, "{}", desc);
}
}
Upvotes: 8