How to assert a specific custom error in tests?

⚓ Rust    📅 2026-01-15    👤 surdeus    👁️ 2      

surdeus

I 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

Read full topic

🏷️ Rust_feed