有一个context可以将可选值转换为anyhow::Error非常方便的。
但是,我们如何在单元测试中测试它?
假设我们有foo这样的:
fn foo(input: i32) -> Result<i32> {
// this only keep odd numbers
let filtered = if input % 2 == 0 { Some(input) } else { None };
filtered.context("Not a valid number")
}
Run Code Online (Sandbox Code Playgroud)
很容易测试它是有效输出,还是输出是错误的。但是我们如何测试来自的错误消息呢context?
mod test {
use super::*;
#[test]
fn test_valid_number() -> Result<()> {
let result = foo(4)?;
assert_eq!(result, 4);
Ok(())
}
#[test]
fn test_invalid_number() -> Result<()> {
let result = foo(3);
assert!(result.is_err());
Ok(())
}
// error[E0599]: no method named `message` found for struct `anyhow::Error` in the current scope
// --> src/main.rs:33:40
// |
// 33 | assert_eq!(result.unwrap_err().message(), "Not a valid number");
// | ^^^^^^^ method not found in `anyhow::Error`
#[test]
fn test_invalid_number_error_message() -> Result<()> {
let result = foo(3);
assert_eq!(result.unwrap_err().message(), "Not a valid number");
Ok(())
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用.chain()and.root_cause()来处理上下文级别,并使用.downcast_ref()orformat!来处理特定的错误。例如,假设您有 2 个级别的上下文。
use anyhow::*;
fn bar(input: i32) -> Result<i32> {
// this only keep odd numbers
let filtered = if input % 2 == 0 { Some(input) } else { None };
filtered.context("Not a valid number")
}
fn foo(input: i32) -> Result<i32> {
return bar(input).context("Handled by bar")
}
Run Code Online (Sandbox Code Playgroud)
在此示例中,链将是错误"Handled by bar"-> "Not a valid number"。
#[test]
fn check_top_error() -> Result<()> {
let result = foo(3);
let error = result.unwrap_err();
// Check top error or context
assert_eq!(format!("{}", error), "Handled by bar");
// Go down the error chain and inspect each error
let mut chain = error.chain();
assert_eq!(chain.next().map(|x| format!("{x}")), Some("Handled by bar".to_owned()));
assert_eq!(chain.next().map(|x| format!("{x}")), Some("Not a valid number".to_owned()));
assert_eq!(chain.next().map(|x| format!("{x}")), None);
Ok(())
}
#[test]
fn check_root_cause() -> Result<()> {
let result = foo(3);
let error = result.unwrap_err();
// Equivalent to .chain().next_back().unwrap()
let root_cause = error.root_cause();
assert_eq!(format!("{}", root_cause), "Not a valid number");
Ok(())
}
Run Code Online (Sandbox Code Playgroud)
现在,您可能想知道我对format!. 事实证明,存在一个更好的解决方案,涉及downcast_ref,但它要求您的上下文实现std::error::Error,但str没有。anyhow下面是直接取自文档的示例。
use anyhow::{Context, Result};
fn do_it() -> Result<()> {
helper().context(HelperFailed)?;
...
}
fn main() {
let err = do_it().unwrap_err();
if let Some(e) = err.downcast_ref::<HelperFailed>() {
// If helper failed, this downcast will succeed because
// HelperFailed is the context that has been attached to
// that error.
}
}
Run Code Online (Sandbox Code Playgroud)
作为旁注,您可能会发现它更易于使用.then(),或者.then_some()适用于基于布尔值if input % 2 == 0 { Some(input) } else { None }创建的情况。Some简单来说,if abc { Some(xyz) } else { None }相当于abc.then(|| xyz)。.then_some()按值传递而不是使用闭包,因此我通常不使用它。
| 归档时间: |
|
| 查看次数: |
2330 次 |
| 最近记录: |