当我用cargo test
运行以下程序时:
use std::panic;
fn assert_panic_func(f: fn() -> (), msg: String) {
let result = panic::catch_unwind(|| {
f();
});
assert!(result.is_err(), msg);
}
macro_rules! assert_panic {
($test:expr , $msg:tt) => {{
fn wrapper() {
$test;
}
assert_panic_func(wrapper, $msg.to_string())
}};
}
fn main() {
println!("running main()");
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn t_1() {
assert_panic!(
panic!("We are forcing a panic"),
"This is asserted within function (raw panic)."
);
// assert_panic!(true, "This is asserted within function (raw true).");
}
}
我得到了预期的输出:
running 1 test
test tests::t_1 ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
如果取消第二条assert_panic!(...)行的注释,然后重新运行cargo test
,则会得到以下输出:
running 1 test
test tests::t_1 ... FAILED
failures:
---- tests::t_1 stdout ----
thread 'tests::t_1' panicked at 'We are forcing a panic', src/lib.rs:29:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'tests::t_1' panicked at 'This is asserted within function (raw true).', src/lib.rs:7:5
failures:
tests::t_1
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
第二个恐慌是合法的,这就是我要寻找的,但是第一个恐慌似乎是由在第一次调用时未触发恐慌的行触发的。
发生了什么,如何解决?
stderr输出
线程'tests :: t_1'对'我们正在引发恐慌'感到恐慌,src / main.rs:30:23
记录是否与紧急情况无关,除非测试失败,否则运行的测试不会显示任何记录的输出。要完全隐藏该文本,您需要使用std::panic::set_hook
单独换出紧急通知挂钩。
std::panic::set_hook
话虽如此,我第二次回答@SCappella有关使用fn assert_panic_func(f:fn()->(), msg: String) {
let previous_hook = panic::take_hook();
// Override the default hook to avoid logging panic location info.
panic::set_hook(Box::new(|_| {}));
let result = panic::catch_unwind(|| {
f();
});
panic::set_hook(previous_hook);
assert!(result.is_err(), msg );
}
的问题。
即使#[should_panic]
陷入紧急状态,该紧急状态的任何输出也会被打印。您在第一次测试中看不到任何东西的原因(在第二次恐慌中被注释掉)是std::panic::catch_unwind
为了更清楚地了解此行为,可以使用cargo test
doesn't print output from successful tests.代替测试。 cargo test
main
运行此给出输出
(playground)
请注意,紧急情况通常会输出到stderr,而不是stdout,因此可以将其过滤掉。
另请参见fn main() {
let _ = std::panic::catch_unwind(|| {
panic!("I don't feel so good Mr. catch_unwind");
});
println!("But the execution of main still continues.");
}
。
我不确定这是否是您要尝试的操作,但是如果要确保测试紧急,请使用thread 'main' panicked at 'I don't feel so good Mr. catch_unwind', src/main.rs:3:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
But the execution of main still continues.
,例如
Suppress panic output in Rust when using panic::catch_unwind
[当时我还不知道单元测试会抑制输出消息。当研究为什么println!(...)在单元测试中不起作用时,我后来意识到抑制输出消息。也许这也可以解释为什么恐慌有时会显示而有时却不会出现。
尽管如此,即使我明确地告诉Rust我希望防止恐慌产生任何影响,恐慌仍会产生输出,这在我看来是不正确的,但是如果Rust做到了这一点,那么不管看上去如何反常,必须忍受它。
我知道#[should_panic]属性,但由于以下两个原因对这种解决方案不满意:
首先,它要求每个测试成为一个单独的函数,而我倾向于将多个紧密相关的测试(许多测试不超过单个assert!(...)语句)放入一个函数中。] >
第二,最好有一个模型来表示每个测试。在我看来,测试表达式是否引起恐慌(或未能引起恐慌)与测试结果是否等于或不等于某个特定值没有什么不同。创建一个模型来表示两个测试对我而言意义更大,因此我希望拥有一个assert_panic!(...)宏,其行为类似于assert!(...)或assert_eq!(... )宏。看来这根本不是Rust可以实现的目标。
感谢您清理。