How do I write a Rust unit test that ensures that a panic has occurred?

I have a Rust function that panics under some condition and I wish to write a test case to validate whether the function is panicking or not. I couldn't find anything except the assert! and assert_eq! macros. Is there some mechanism for testing this?

I could spawn a new task and checking whether that task panics or not. Does it make sense?


Returning a Result<T, E> is not suitable in my case.

I wish to add support for the Add trait to a Matrix type I am implementing. The ideal syntax for such addition would look like:

let m = m1 + m2 + m3;

where m1, m2, m3 are all matrices. Hence, the result type of add should be Matrix. Something like the following would be too cryptic:

let m = ((m1 + m2).unwrap() + m3).unwrap()

At the same time, the add() function needs to validate that the two matrices being added have same dimension. Thus, add() needs to panic if the dimensions don't match. The available option is panic!().

Answers


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()).


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.


As an addendum: The solution proposed by @U007D also works in doctests:

/// My identity function that panic for an input of 42.
///
/// ```
/// assert_eq!(my_crate::my_func(23), 23);
///
/// let result = std::panic::catch_unwind(|| my_crate::my_func(42));
/// assert!(result.is_err());
/// ```
pub fn my_func(input: u32) -> u32 {
    if input == 42 {
        panic!("Error message.");
    } else {
        input
    }
}

Need Your Help

What is the real purpose of Base64 encoding?

encoding base64

Why do we have Base64 encoding? I am a beginner and I really don't understand why would you obfuscate the bytes into something else (unless it is encryption). In one of the books I read Base64 enco...

How to set a launch screen image in Xcode

ios iphone xcode swift

I am working on a iPhone App and I am trying to set a launch screen image.