如何在异步代码中使用panic::catch_unwind?

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

使用同步代码时,我可以像这样使用

panic::catch_unwind

#[actix_rt::test]
async fn test_sync() -> Result<(), Error> {
    println!("before catch_unwind");
    let sync_result = panic::catch_unwind(|| {
        println!("inside sync catch_unwind");
        panic!("this is error")
    });
    println!("after catch_unwind");

    assert!(sync_result.is_ok());

    Ok(())
}

在使用

catch_unwind
块内执行的异步代码时,如何执行相同的操作?我不知道如何运行该块,同时也能够在该块之后运行一些代码并最终断言结果。

这是我到目前为止所拥有的:

#[actix_rt::test]
async fn test_async() -> Result<(), Error> {
    println!("before catch_unwind");
    let async_result = panic::catch_unwind(|| async {
        println!("inside async catch_unwind");
        panic!("this is error")
    }).await;
    println!("after catch_unwind");

    assert!(async_result.is_ok());

    Ok(())
}
rust async-await
3个回答
11
投票

不会尝试直接使用它们。相反,请使用

FutureExt::catch_unwind
StreamExt::catch_unwind

use futures::FutureExt; // 0.3.5

#[tokio::test]
async fn test_async() -> Result<(), Box<dyn std::error::Error>> {
    println!("before catch_unwind");

    let may_panic = async {
        println!("inside async catch_unwind");
        panic!("this is error")
    };

    let async_result = may_panic.catch_unwind().await;

    println!("after catch_unwind");

    assert!(async_result.is_ok());

    Ok(())
}

4
投票

我遇到了这个问题,Shepmaster 的答案确实部分有效。我遇到了很多错误,其中包含有关展开和不安全传输变量的非常复杂的描述。在您对已接受答案的评论中,您也解决了这个问题。

下面的解决方法解决了我的问题。我不建议在测试之外使用它,因为这种方式可能很昂贵。它使用互斥体和当前运行时(句柄)。

fn main() {}

#[cfg(test)]
mod test {
    #[tokio::test]
    async fn test() {
        env_logger::init();
        // This variable represents your complex type
        // Note that this can be a combination of types if you use a tuple or something else
        let my_complex_type = 1;

        // Wrap it all in a std::sync::Mutex
        let mutex = std::sync::Mutex::new(my_complex_type);

        // Pass the mutex in the panic unwind
        assert!(std::panic::catch_unwind(|| {
            // Now you can work with your complex type
            let my_complex_type = mutex.lock().unwrap();

            // Enter the runtime
            let handle = tokio::runtime::Handle::current();

            handle.enter();

            futures::executor::block_on(do_something(*my_complex_type));
        }).is_err());
    }

    async fn do_something(t: i32) {
        panic!();
    }
}

0
投票

为了避免一百个左右的“可能无法安全地跨展开边界传输”错误的“乐趣”,您可以使用

AssertUnwindSafe
包装器类型 告诉编译器仅使用 STFU。官方文档中确实没有一个很好的
async
友好示例,但以下是我如何获得需要运行后清理才能工作的异步测试:

#[actix_rt::test]                                                                                                                                                                                                                              
async fn password_auth() {                                                                                                                                                                                                                     
    let b = Browser::new().await;                                                                                                                                                                                                              
                                                                                                                                                                                                                                               
    let result = AssertUnwindSafe(run_password_auth(b.clone())).catch_unwind().await;                                                                                                                                                          
                                                                                                                                                                                                                                               
    b.close().await;                                                                                                                                                                                                                           
                                                                                                                                                                                                                                               
    if let Err(e) = result {                                                                                                                                                                                                                   
        panic!("{:?}", e);                                                                                                                                                                                                                     
    }                                                                                                                                                                                                                                          
}

async fn run_password_auth(b: Browser) {
    // .await away!
}                                                                                                                                                                                                                                              
© www.soinside.com 2019 - 2024. All rights reserved.