How to assert a specific custom error in tests?
⚓ Rust 📅 2026-01-15 👤 surdeus 👁️ 2I have a custom error type defined with thiserror:
#[derive(Debug, thiserror::Error)]
pub enum ProxyError {
#[error("invalid host: {0}")]
InvalidHost(Box<str>),
#[error("failed to resolve IP address from host: {0}")]
DnsResolution(#[from] io::Error),
...
}
A FromStr implementation returns this error type:
impl std::str::FromStr for Host {
type Err = ProxyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if !hostname_validator::is_valid(s) {
return Err(ProxyError::InvalidHost(s.into()));
}
Ok(Host(s.into()))
}
}
In tests, I want to assert that parsing an invalid string returns a specific variant of my custom error:
#[test]
fn test_invalid_host_from_str() {
let res = Host::from_str("108.162.198.200:80");
assert!(res.is_err());
assert_eq!(
res.unwrap_err(),
ProxyError::InvalidHost("108.162.198.200:80".into())
);
}
Yes, the compiler complains:
binary operation `==` cannot be applied to type `ProxyError`
This is expected, since some variants wrap types like std::io::Error which do not implement PartialEq, so I cannot simply #[derive(PartialEq)] for the whole enum.
Manually implementing PartialEq for each variant feels verbose and error-prone, especially as the enum grows.
idiomatic-way-of-testing-result-t-error
Stackoverflow - How do you test for a specific Rust error?
One workaround I found is using the matches! macro:
#[test]
fn test_invalid_host_from_str() {
let res = Host::from_str("108.162.198.200:80");
assert!(res.is_err());
assert!(matches!(
res.unwrap_err(),
ProxyError::InvalidHost(_)
));
}
This works, but it feels slightly imprecise:
- it does not check the inner value
assert_matches!would read better, but it is still nightly-only
Question:
Is there a more idiomatic or expressive way to assert specific error variants in tests in stable Rust?
Are there any well-accepted patterns or crates for this use case?
3 posts - 3 participants
🏷️ Rust_feed