Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to assert io errors in Rust?

Tags:

There are plenty of tutorials showing how to write unit tests in Rust. I've read dozen and all of them focuses on asserting values in case of success. Situation doesn't seem so straight forward in case of an error. Errors do not implement PartialEq trait by default so you can't use assert_eq! macro. Also, some functions may return multiple variants of error depending on what kind of issue occurred (ex. io::Error which may be of different kind.) I could just check if error occurred or not but it doesn't seem enough.

Example below.

fn parse_data(input: i32) -> Result<i32, io::Error> {     match input {         0 => Ok(0),         _ => Err(io::Error::new(io::ErrorKind::InvalidData, "unexpected number"))     } }  #[test] fn test_parsing_wrong_data() {     let result = parse_data(1);     assert!(result.is_err());     let got = result.unwrap_err();     let want = io::Error::new(io::ErrorKind::InvalidData, "unexpected number");      // compilation error here: binary operation `==` cannot be applied to type `std::io::Error`     assert_eq!(want, got); } 

I assume this is not idiomatic approach, since it's not compiling. Hence the question - what is a proper and idiomatic approach in similar situation?

like image 713
wst Avatar asked Jul 27 '19 16:07

wst


People also ask

How do you fail a test in Rust?

If you want the test to fail, just put #[should_fail] under #[test] . To run unit tests, add either the --test or --cfg test flag to the command.

How do you test a function in Rust?

At its simplest, a test in Rust is a function that's annotated with the test attribute. Attributes are metadata about pieces of Rust code; one example is the derive attribute we used with structs in Chapter 5. To change a function into a test function, add #[test] on the line before fn .

What is assert in Rust?

Macro std::assertAsserts that a boolean expression is true at runtime. This will invoke the panic! macro if the provided expression cannot be evaluated to true at runtime.

Where do I place unit tests in Rust?

You'll put unit tests in the src directory in each file with the code that they're testing. The convention is to create a module named tests in each file to contain the test functions and to annotate the module with cfg(test) .


2 Answers

TL;DR: Error should implement PartialEq

Result<T, E> only implements PartialEq when T and E also implement PartialEq, but io::Error doesn't. alex confirm that cause io::Error takes an extra error that implements dyn Error, allowing the user to add extra information lead std to not implement PartialEq.


I see a lot of answer and comment that seem a lot of peoples use io::Error to create their own error. This is NOT a good practice, io::Error should be used only if you deal yourself with io. There is the Error Handling Project Group if you want to learn and share your view about error in Rust.

For now there is some common crate in Rust to make your own error (feel free to add crate):

  • snafu (my favorite)
  • thiserror
  • anyhow
  • quick-error

I don't totally agree but here a good guide about Error in Rust.


Anyway, the solution that you probably want in your case is just to compare the ErrorKind value. As ErrorKind implements PartialEq this will compile with assert_eq()

use std::io;  fn parse_data(input: i32) -> Result<i32, io::Error> {     match input {         0 => Ok(0),         x => Err(io::Error::new(             io::ErrorKind::InvalidData,             format!("unexpected number {}", x),         )),     } }  #[test] fn test_parsing_wrong_data() {     let result = parse_data(1).map_err(|e| e.kind());     let expected = Err(io::ErrorKind::InvalidData);     assert_eq!(expected, result); } 
like image 196
Stargateur Avatar answered Oct 02 '22 14:10

Stargateur


I solved it with this

assert!(matches!(result, Err(crate::Error::InvalidType(t)) if t == "foobar"));  

This solution doesn't require PartialEq for the Error, but still allows me to compare with the variant contents.

If you don't care about the contents of the variant, then its just

assert!(matches!(result, Err(crate::Error::InvalidType(_))));  
like image 27
scoopr Avatar answered Oct 02 '22 15:10

scoopr