Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible for the assert_eq macro to show a diff when two strings aren't equal?

I'm writing some tests for a language tokenizer and I'm comparing a JSON-serialized version of the tokenization produced by the tokenizer with a serialization of a known-good tokenization. So I have some tests like this:

#[test]
fn test_tokenize() {
    let actual_token_json = /* string */;
    let expected_token_json = /* string */;

    assert_eq!(actual_token_json, expected_token_json);
}

However, when those tests fail, cargo test just says that the JSON strings weren't equal and prints them both out, which isn't that useful. Since the JSON strings are both pretty-printed, it would be more helpful if cargo test printed out a line diff of the two strings. Is there any way I could do this?

Update: Someone edited the title of this post but deleted an important piece of information: I'm looking for a line diff when two strings aren't equal. One of the answers I'm seeing here is for the more general question of getting a line and character diff of the debug representations of two values when they're not equal. That's either more than I need or not what I need. For example, the diff of the debug representations of two strings won't give me a line diff but a large, convoluted character diff (since the lines show up in the debug representation as escaped "\n" tokens).

like image 777
David Sanders Avatar asked Sep 12 '19 22:09

David Sanders


2 Answers

Yes, but not with the assert_eq macro provided by the standard library. You could use the pretty_assertions crate which was designed to do exactly what you are looking for.

diff


Actually, the built-in macro could take more than two arguments to provide more information about the assertion by extending it with a custom message. So technically, if you provide some sort of diff'ing implementation yourself or an external one, which is either Display or Debug, you could then pass it to the macro. Strictly speaking in some ways this macro is also usable for such a thing.

like image 200
Peter Varo Avatar answered Sep 24 '22 16:09

Peter Varo


See the update in my post as to why pretty_assertions doesn't quite work for my particular problem. Instead, I ended up just using the difference library that pretty_assertions uses internally. I just made a wrapper struct for difference::Changeset so that I could override its fmt::Display implementation:

fn prefix_lines(prefix: &str, lines: &str) -> String {
    lines
        .lines()
        .map(|i| [prefix, i].concat())
        .collect::<Vec<String>>()
        .join("\n")
}

pub struct Diff(difference::Changeset);

impl Diff {
    pub fn new(left: &str, right: &str) -> Self {
        Self(difference::Changeset::new(left, right, "\n"))
    }
}

impl fmt::Display for Diff {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for d in &self.0.diffs {
            match *d {
                difference::Difference::Same(ref x) => {
                    write!(f, "{}{}", prefix_lines(" ", x), self.0.split)?;
                }
                difference::Difference::Add(ref x) => {
                    write!(f, "\x1b[92m{}\x1b[0m{}", prefix_lines("+", x), self.0.split)?;
                }
                difference::Difference::Rem(ref x) => {
                    write!(f, "\x1b[91m{}\x1b[0m{}", prefix_lines("-", x), self.0.split)?;
                }
            }
        }
        Ok(())
    }
}

Then I just have a macro, as in the pretty_assertions implementation, that creates an instance of that struct and string formats it inside of a panic message when the assertion fails.

like image 32
David Sanders Avatar answered Sep 24 '22 16:09

David Sanders