Reputation: 3787
I have a function that returns Result<(), Box<dyn Error>>
. I am writing a test case for this function where the function should return an error of variant VerifyError::LinearCombinationError
(playground):
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
#[derive(Debug, PartialEq, Eq)]
enum VerifyError {
LinearCombination,
}
impl Error for VerifyError {}
impl Display for VerifyError {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
fn return_result() -> Result<(), Box<dyn Error>> {
Err(Box::new(VerifyError::LinearCombination))
}
pub fn main() {
let res = return_result();
println!("debug print is: {:?}", res);
// Program does not compile when below line is not commented out
// assert_eq!(Some(VerifyError::LinearCombination), res.err());
}
Uncommenting the line gives the error message:
error[E0308]: mismatched types
--> src/main.rs:26:5
|
26 | assert_eq!(Some(VerifyError::LinearCombination), res.err());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `VerifyError`, found struct `Box`
|
= note: expected enum `Option<VerifyError>`
found enum `Option<Box<dyn std::error::Error>>`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
I am using Rust 1.53.0.
Upvotes: 7
Views: 3868
Reputation: 116
You can also use the is_ok
and is_err
functions of the Result
type to test the type of variant if you don't care about the specific error itself.
An example would be:
#[cfg(test)]
mod tests {
#[test]
fn should_return_error() {
assert!(super::return_result().is_err());
}
}
Note: It's assumed the return_result
function is defined in the parent module. Therefore, it can be accessed with super
.
Upvotes: 0
Reputation: 430941
Use Error::is
to test if the error trait object has a specific concrete type or Error::downcast
to convert it to that concrete type:
use std::error::Error;
fn example() -> Result<(), Box<dyn Error>> {
Err(std::io::Error::last_os_error().into())
}
#[test]
fn is_from_io_error() {
let e = example().err().unwrap();
assert!(e.is::<std::io::Error>());
let _e = e.downcast::<std::io::Error>().unwrap();
}
If you care about the specific error, I'd encourage you to avoid using trait objects. The point of trait objects is that you don't need to know what the concrete type is.
Instead, you could use a library that helps you construct error types, such as my SNAFU. This usage shows wrapping errors in an enum that can be pattern matched to test the specific type:
use snafu::{Snafu, ResultExt}; // snafu = "0.7.0-beta.0"
#[derive(Debug, Snafu)]
enum Error {
Example { source: std::io::Error }
}
fn example() -> Result<(), Error> {
Err(std::io::Error::last_os_error()).context(ExampleSnafu)
}
#[test]
fn is_from_io_error() {
let e = example();
assert!(matches!(e, Err(Error::Example { .. })))
}
Upvotes: 12