NUnit是一个C#单元测试框架,允许您编写如下代码:
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)
( ie 在库中的行而不是用户的代码)。我知道这种行为是正常的,但我会有更有用的信息。
如何在恐慌中指出正确的行号?
答案 0 :(得分:2)
没有直接的方法来调整panic!
打印的行号。
有a proto-RFC添加一个属性,允许某些方法从回溯中“隐藏”。这样的属性也可能会影响行号,但目前还不清楚。
How to write a panic! like macro in Rust?描述了如何编写自己的panic!
宏,但它选择拆除整个过程,而不仅仅是当前线程。
重要的是你只想控制消息,这可以通过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
内设置恐慌处理程序。
答案 1 :(得分:0)
您可能有兴趣使用spectral,这是一个为Rust提供流畅测试断言的库。如果您按照其他人的建议查看他们的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