How do I write a Rust unit test that ensures that a panic has occurred?
Use following catch_unwind_silent
instead of regular catch_unwind
to achieve silence in output for expected exceptions:
use std::panic;
fn catch_unwind_silent<F: FnOnce() -> R + panic::UnwindSafe, R>(f: F) -> std::thread::Result<R> {
let prev_hook = panic::take_hook();
panic::set_hook(Box::new(|_| {}));
let result = panic::catch_unwind(f);
panic::set_hook(prev_hook);
result
}
If you want to assert that only a specific portion of the test function fails, use std::panic::catch_unwind()
and check that it returns an Err
, for example with is_err()
. In complex test functions, this helps ensure that the test doesn't pass erroneously because of an early failure.
Several tests in the Rust standard library itself use this technique.
You can find the answer in testing section of the Rust book. More specifically, you want #[should_panic]
attribute:
#[test]
#[should_panic]
fn test_invalid_matrices_multiplication() {
let m1 = Matrix::new(3, 4); // assume these are dimensions
let m2 = Matrix::new(5, 6);
m1 * m2
}
As Francis Gagné mentioned in his answer, I also find the #[should_panic]
attribute to not be fine-grained enough for more complex tests--for example, if my test setup fails for some reason (i.e. I've written a bad test), I do want a panic to be considered a failure!
As of Rust 1.9.0, std::panic::catch_unwind()
is available. It allows you to put the code you expect to panic into a closure, and only panics emitted by that code will be considered expected (i.e. a passing test).
#[test]
fn test_something() {
... //<-- Any panics here will cause test failure (good)
let result = std::panic::catch_unwind(|| <expected_to_panic_operation_here>);
assert!(result.is_err()); //probe further for specific error type here, if desired
}
Note it cannot catch non-unwinding panics (e.g. std::process::abort()
).