Проблемы с использованием pani c :: catch_unwind в макро-тесте контекста для паники в модульных тестах - PullRequest
0 голосов
/ 01 апреля 2020

Когда я запускаю следующую программу с 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_pani c! (. ..) и, повторно запустив 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

Вторая пани c является законной, и это то, что я ищу, но первая пани c Кажется, что это вызвано линией, которая не вызывала пани c при первом вызове.

Что происходит, и как мне это исправить?

Ответы [ 3 ]

0 голосов
/ 01 апреля 2020

Вывод stderr

thread 'tests :: t_1' в панике из-за 'Мы заставляем pani c', src / main.rs: 30: 23

регистрируется независимо от того, был ли обнаружен pani c, выполнение теста просто не показывает никакого зарегистрированного вывода, пока тест не завершится неудачей. Чтобы полностью исключить этот текст, вам необходимо отдельно заменить хук уведомления pani c, используя std::panic::set_hook.

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 );
}

Все это говорит, что я второй ответ @ SCappella о используя #[should_panic].

0 голосов
/ 04 апреля 2020

В то время я не знал, что модульные тесты будут подавлять выходные сообщения. Позже я узнал о подавлении выходных сообщений при исследовании того, почему println! (...) не будет работать в модульных тестах. То, что это также может быть ответом на то, почему паника иногда отображается, а иногда и не имеет смысла.

Тем не менее, мне кажется извращенным, что паника выдает результат, даже когда я явно говорю Rust, что я буду sh, чтобы предотвратить пани c от какого-либо эффекта, но если это то, что делает Rust, каким бы извращенным это ни казалось, тогда нужно с этим смириться.

Я знал о # [should_panic ], но не был доволен этим решением по двум причинам:

Во-первых, он требует, чтобы каждый тест становился отдельной функцией, в то время как я склонен помещать ряд тесно связанных тестов (многие из тестов не более одного утверждения assert! (...)) в одну функцию.

Во-вторых, было бы неплохо иметь одну модель для express каждого теста. На мой взгляд, тестирование того, вызывает ли выражение пани c (или не вызывает пани c), ничем не отличается от проверки того, равен ли результат некоторому конкретному значению или не равен ему. Для меня гораздо больше смысла создавать одну модель для express обоих тестов, поэтому я хочу иметь макрос assert_pani c! (...), который ведет себя аналогично assert! (...) или assert_eq ! (...) макрос. Похоже, что это просто недостижимая цель в Rust.

Спасибо за разъяснение.

0 голосов
/ 01 апреля 2020

Даже если std::panic::catch_unwind ловит пани c, любой вывод из этой пани c будет напечатан. Причина, по которой вы ничего не видите в первом тесте (с закомментированным вторым pani c), заключается в том, что cargo test не выводит результаты успешных тестов.

To чтобы увидеть это поведение более четко, вы можете использовать main вместо теста. (детская площадка)

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.

Обратите внимание, что паника обычно выводится в stderr, а не в stdout, поэтому можно отфильтровать эти out.

См. также Подавление вывода pani c в Rust при использовании panic::catch_unwind.


Я не уверен, что это то, что вы ' пытается сделать это, но если вы хотите убедиться, что тест паникует, используйте атрибут should_panic. Например,

#[test]
#[should_panic]
fn panics() {
    panic!("Successfully panicked");
}
...