在宏上下文测试中使用panic :: catch_unwind的问题在单元测试中出现了恐慌

问题描述 投票:0回答:3

当我用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

第二个恐慌是合法的,这就是我要寻找的,但是第一个恐慌似乎是由在第一次调用时未触发恐慌的行触发的。

发生了什么,如何解决?

unit-testing rust macros panic
3个回答
0
投票

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 ); } 的问题。


0
投票

即使#[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

0
投票

[当时我还不知道单元测试会抑制输出消息。当研究为什么println!(...)在单元测试中不起作用时,我后来意识到抑制输出消息。也许这也可以解释为什么恐慌有时会显示而有时却不会出现。

尽管如此,即使我明确地告诉Rust我希望防止恐慌产生任何影响,恐慌仍会产生输出,这在我看来是不正确的,但是如果Rust做到了这一点,那么不管看上去如何反常,必须忍受它。

我知道#[should_panic]属性,但由于以下两个原因对这种解决方案不满意:

首先,它要求每个测试成为一个单独的函数,而我倾向于将多个紧密相关的测试(许多测试不超过单个assert!(...)语句)放入一个函数中。] >

第二,最好有一个模型来表示每个测试。在我看来,测试表达式是否引起恐慌(或未能引起恐慌)与测试结果是否等于或不等于某个特定值没有什么不同。创建一个模型来表示两个测试对我而言意义更大,因此我希望拥有一个assert_panic!(...)宏,其行为类似于assert!(...)或assert_eq!(... )宏。看来这根本不是Rust可以实现的目标。

感谢您清理。

© www.soinside.com 2019 - 2024. All rights reserved.