代码之家  ›  专栏  ›  技术社区  ›  Boiethios

有可能改变整个恐慌信息吗?

  •  4
  • Boiethios  · 技术社区  · 9 年前

    Assert.That(someInt, Is.EqualTo(42));
    Assert.That(someList, Has.Member(someMember));
    

    我喜欢这种代码,因为它看起来像英语,很容易阅读。


    我在玩Rust,看看能否创建一个能给人同样感觉的库:

    use std::fmt::Debug;
    
    struct Is;
    
    enum Verb<T> {
        EqualTo(T),
    }
    
    impl Is {
        fn equal_to<T>(&self, obj: T) -> Verb<T> {
            Verb::EqualTo(obj)
        }
    }
    
    #[allow(non_upper_case_globals)]
    const is: Is = Is{};
    
    fn assert_that<T: Eq + Debug>(obj: T, verb: Verb<T>) {
        match verb {
            Verb::EqualTo(rhs)    => assert_eq!(obj, rhs),
        }
    }
    
    fn main() {
        assert_that(42, is.equal_to(42));
        assert_that(42, is.equal_to(0));
    }
    

    这很好,但有一点:当代码在 assert_that(42, is.equal_to(0)) ,恐慌给出的线是 assert_eq!(obj, rhs) (

    2 回复  |  直到 8 年前
        1
  •  2
  •   Shepmaster Tim Diekmann    9 年前

    panic! 打印。

    a proto-RFC

    How to write a panic! like macro in Rust? 惊恐 宏,但它选择分解整个过程,而不仅仅是当前线程。


    panic::set_hook

    use std::cell::Cell;
    
    thread_local! {
        static ASSERT_LOCATION: Cell<Option<(&'static str, u32)>> = Cell::new(None)
    }
    
    fn report_my_error(info: &std::panic::PanicInfo) {
        match info.location() {
            Some(location) => {
                let file = location.file();
                let line = location.line();
                println!("The panic actually happened at: {}, {}", file, line);
            }
            None => println!("I don't know where the panic actually happened"),
        }
    
        ASSERT_LOCATION.with(|location| if let Some((file, line)) = location.get() {
            println!(
                "But I'm going to tell you it happened at {}, {}",
                file,
                line
            );
        });
    
        if let Some(msg) = info.payload().downcast_ref::<&str>() {
            println!("The error message was: {}", msg);
        }
    }
    
    #[test]
    fn alpha() {
        std::panic::set_hook(Box::new(report_my_error));
    
        ASSERT_LOCATION.with(|location| {
            location.set(Some((file!(), line!())));
        });
    
        panic!("This was only a test")
    }
    

    您需要确保在每次测试中设置了紧急处理程序,然后设置位置信息。您可能还需要更新紧急处理程序,将位置信息设置回 None 以避免线程之间的位置信息泄漏。

    您可能希望编写自己的宏,用户可以在测试中使用该宏隐式设置行号。类似于此的语法可以为该设置代码提供生存空间:

    assert_that!(42, is.equal_to(0));
    

    assert_that(file!(), line!(), 42, is.equal_to(0));
    

    我可能会把恐慌处理程序放在 assert_that

        2
  •  0
  •   Shepmaster Tim Diekmann    9 年前

    您可能有兴趣使用 spectral ,一个库,提供流畅的锈病测试断言。如果你看看他们 implementation line!() file!() assert_that! 库中定义的宏。

    用法如下:

    #[test]
    fn example() {
        assert_that!(2).is_equal_to(4);
    }
    

    failures:
    
    ---- utils::tests::example stdout ----
            thread 'utils::tests::example' panicked at '
            expected: <4>
            but was: <2>
    
            at location: src/utils.rs:344
    ', /home/example/.cargo/registry/src/github.com-1ecc6299db9ec823/spectral-0.6.0/src/lib.rs:343
    note: Run with `RUST_BACKTRACE=1` for a backtrace.
    
    
    failures:
        utils::tests::example
    
    test result: FAILED. 61 passed; 1 failed; 0 ignored; 0 measured
    
    推荐文章