How do you assert enums?


#1

I have an enum for Opcodes, like so:

enum Opcode {
  Add,
  Sub,
  Mul,
  Div,
  etc...
};

And I’m trying to test that a struct containing the opcode as a field has the correct enum. How do I assert it in a test? The following doesn’t work.

fn x_n_test() {
    let node = x_n();
    assert_eq!(node.opcode, Opcode::X);
}

#2

assert_eq! needs (naturally) an == operator, thus also an PartialEq implementation for operands. For enums, prepending #[derive(PartialEq)] is the easiest way to get it.


#3

I’m pretty new to Rust. While it makes sense I need to implement a == operator, I’m not sure what you mean by PartialEq implementation. Do you have a short example?


#4

Ah, probably that needs a knowledge about traits. :slight_smile: If you don’t know about traits, for now, you can just assume that == needs the following:

#[derive(PartialEq)] // <- this line has been added
enum Opcode {
    ...
}

(You can also get > or < with #[derive(PartialOrd)]. There are tons of other things like this available with #[derive]. Not every enum can be empowered with this way though, and that’s when you need to understand traits…)


#5

Ah, it’s like a preprocessor that adds methods. I’ll look into derives. Thanks for the lead!


#6

There’s also a matches! macro in the matches crate:

assert!(matches!(node.opcode, Opcode::X));

#7

Nice! But with it, I get an error.

src/libfab/tree/node.rs:603:51: 603:52 error: unexpected end of macro invocation
src/libfab/tree/node.rs:603         assert_eq!(matches!(node.opcode, Opcode::X));
                                                                              ^

Seems to be pointing to the second to last parens.


#8

Sorry, I meant assert!. The unfortunate problem with macros is that the error messages are terrible. Also, it appears you need to import the _tt_as_expr_hack macro as well…

#[macro_use(matches, _tt_as_expr_hack)]
extern crate matches;

enum Opcode {
    X,
    Y,
}

#[test]
fn it_works() {
    assert!(matches!(Opcode::X, Opcode::X));
}

#9

Newbie here, I just start to learn how to create test and it worked after add #[derive(PartialEq, Debug)], here is my complete example.

#[derive(PartialEq, Debug)]
enum Language {
    ENGLISH,
    PORTUGUESE,
}

fn select_language(language: Language) -> Language {
    match language {
        Language::ENGLISH => println!("English selected"),
        Language::PORTUGUESE => println!("Portuguese selectionado"),
    }

    language
}

fn main() {
    select_language(Language::ENGLISH);
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn internal() {
        assert_eq!(Language::ENGLISH, select_language(Language::ENGLISH));
        assert_eq!(Language::PORTUGUESE, select_language(Language::PORTUGUESE));
    }
}

#10

deriving PartialEq is the best solution in this specific case, though another hack that doesn’t require it (nor any other macro) would be

enum Opcode {
    Add,
    Mul,
    HaltAndCatchFire,
}
fn main() {
    let opcode = Opcode::Mul;
    assert!(if let Opcode::Add = opcode { true } else { false })
}

#11

While being ofc correct, I think those hacks are misleading. The best way to solve the problem imo is deriving PartialEq.
This allows you to use == with your enum, which might be useful apart from this usecase as well.

a == b in rust is just syntactic sugar for std::cmp::PartialEq::eq(a, b)
So in order to be able to use == or assert_eq!, you have to tell Rust that you implement this PartialEq trait that provides this method. You could either do this manually, or tell Rust to just do it the “default” way with this #[derive(PartialEq)] line before your enum declaration


#12

Another alternative is the assert_matches crate. This provides a better error message via Debug if the match fails, which is very useful when testing enums that contain data:

#[cfg(test)]
#[macro_use]
extern crate assert_matches;

#[derive(Debug)]
pub enum Opcode {
    X { i: i32 },
    Y,
}

#[test]
fn it_works() {
    let a = Opcode::X { i: 5 };
    assert_matches!(a, Opcode::X { i: 1..=4 });
}

thread 'it_works' panicked at 'assertion failed: `X { i: 5 }` does not match `Opcode::X { i: 1 ..=4 }`', src/lib.rs:14:5