Подтвердить что ты не робот

Как написать Rust unit test, который гарантирует, что произошла паника?

У меня есть функция Rust, которая panic при некоторых условиях, и я хочу написать тестовый пример, чтобы проверить, паникует ли функция или нет. Я не мог найти ничего, кроме макросов assert! и assert_eq!. Есть ли какой-нибудь механизм для тестирования этого?

Я мог бы создать новую задачу и проверить, паникует ли эта задача или нет. Имеет ли это смысл?


Возврат Result<T, E> не подходит в моем случае.

Я хочу добавить поддержку признака Add к типу Matrix, который я реализую. Идеальный синтаксис для такого добавления будет выглядеть так:

let m = m1 + m2 + m3;

где m1, m2, m3 - все матрицы. Следовательно, тип результата Add должен быть Matrix. Что-то вроде следующего было бы слишком загадочным:

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

В то же время функция add() должна проверять, что две добавляемые матрицы имеют одинаковую размерность. Таким образом, add() должен паниковать, если размеры не совпадают. Доступная опция panic!().

4b9b3361

Ответ 1

Вы можете найти ответ в разделе testing в книге 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
}

Ответ 2

Как упомянул Фрэнсис Гань в своем ответе, я также нахожу, что атрибут #[should_panic] недостаточно детализирован для более сложных тестов - например, если по какой-то причине моя установка теста не удалась (т.е. я написал плохой тест Я хочу, чтобы паника считалась неудачей!

Начиная с Rust std::panic::catch_unwind(), доступна 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()).

Ответ 3

Если вы хотите утверждать, что только определенная часть тестовой функции дает сбой, используйте std::panic::catch_unwind() и проверьте, что он возвращает Err, например, с помощью is_err(). В сложных тестовых функциях это помогает гарантировать, что тест не пройдет ошибочно из-за раннего сбоя.

Несколько тестов в самой стандартной библиотеке Rust используют эту технику.

Ответ 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
    }
}