如何编写Rust单元测试以确保发生恐慌?


77

我有一个panic在某些情况下处于工作状态的Rust函数,我希望编写一个测试用例来验证该函数是否惊慌。除了assert!assert_eq!宏,我什么也找不到。有测试这种情况的机制吗?

我可以产生一个新任务,并检查该任务是否紧急。是否有意义?


返回aResult<T, E>不适合我的情况。

我希望在我正在实现AddMatrix类型中增加对特征的支持。这种添加的理想语法如下:

let m = m1 + m2 + m3;

其中m1m2m3都是矩阵。因此,的结果类型add应为Matrix。像下面这样的东西太神秘了:

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

同时,该add()功能需要验证所添加的两个矩阵的维数相同。因此,add()如果尺寸不匹配,则需要惊慌。可用的选项是panic!()

Answers:


110

您可以在Rust书的“测试”部分找到答案。更具体地说,您需要#[should_panic]属性:

#[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
}

37
值得一提的是,您可以为恐慌文本添加检查:#[should_panic(expected = "assertion failed")]
phss

1
在此快速说明一下,根据您的IDE和环境,来自恐慌的堆栈跟踪可能仍会出现在输出中,但测试仍会通过。我花了一分钟才意识到自己#[should_panic]实际上在工作。当您从命令行运行通用cargo test命令时,您会注意到它吞没了恐慌,并显示为ok
ragona

44

弗朗西斯加涅在他的回答中提到,我也找到了#[should_panic]属性不细粒度够更复杂的测试-例如,如果由于某种原因,我的测试设置失败(即我写了一个坏的测试),我希望惊慌被视为失败!

从Rust 1.9.0开始std::panic::catch_unwind()可用。它使您可以将您希望惊慌的代码放入闭包中,并且只有代码发出的恐慌才被认为是期望的(即通过测试)。

#[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
}

请注意,它无法捕捉非平息的恐慌(例如std::process::abort())。


17

如果要断言测试功能仅特定部分失败,请使用std::panic::catch_unwind()并检查其是否返回Err,例如is_err()。在复杂的测试功能中,这有助于确保测试不会因早期失败而错误通过。

Rust标准库本身中的一些测试都使用此技术。


4
是否应该为此提供一个assert_failsassert_panics宏?
dhardy 2015年

2
您也可以使用unwrap_err
2013年

#[no_std]环境中有什么方法可以做到这一点?我正在寻找创建一个assert_panics!通用的宏。
jhpratt

4

作为附录:@ U007D提出的解决方案也可以在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
    }
}

4

使用以下命令catch_unwind_silent代替常规命令,catch_unwind以实现预期异常的输出静音:

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
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.