Assert* macroses and assert messages for tests


#1

For python exist pytest module with great assertion messages. For example instead self.assert_equals(a, b) it allow write assert a == b with same informative messages:

standard unittest assertion for simple variable:

        a = 1
        b = 2
>       self.assertEqual(a, b)
E       AssertionError: 1 != 2

pytest assertion for simple variable:

        a = 1
        b = 2
>       assert a == b
E       AssertionError: assert 1 == 2

If I run assert a == b without pytest it just show assertion error without variable information and this behaviour the same for rust. Rust provide assert! and assert_eq! macro, but not assert_ne!, assert_gt!, assert_ge!, assert_lt!, assert_le!, however anybody can implement it himself, but I think that more readable use assert!(a == b), assert!(a != b), assert!(a > b), assert!(a >= b), assert!(a < b), assert(a <= b) instead.

As I understood rust macro not allow detect operators ==, !=, >=, >, <, <= to handle this implementation.

Also messages:

#[test]
fn assert_eq_test() {
    let a = 1i64;
    let b = 2i64;
    assert_eq!(a, b);
}

---- assert_eq_test stdout ----
    task 'assert_eq_test' failed at 'assertion failed: `(left == right) && (right == left)` (left: `1`, right: `2`)', /proj/paht/tests/test_file.rs:103

And:

#[test]
fn assert_test() {
    let a = 1i64;
    let b = 2i64;
    assert!(a == b);
}

---- assert_test stdout ----
    task 'assert_test' failed at 'assertion failed: a == b', /proj/path/tests/test_bbox.rs:95

I think it also will be more readable write what line got assertion code part, for example:

---- assert_eq_test stdout ----
    task 'assert_eq_test' failed at 'assertion failed' /proj/paht/tests/test_file.rs:103:
    >    assert_eq!(a, b);
    E    assertion failed: 1 == 2

But as I uderstood it also can’t be implemented with macro now.

What do you think about this?


#2

BTW, assert actually also takes a format string & arguments, so you can have a custom message: e.g. assert!(a < b, "failed: {} < {}, note: {} and {}", a, b, x, y).

Obviously this is more work than calling assert_lt!(a, b), but it covers many use-cases, especially ones for which a specialised macro would be silly, e.g. this test has information that is very relevant to a test failure, but is only needed there and so would be strange to express via a macro.


#3

Assertion message formating is really great and can cower any cases. However special and single cases really doesn’t deserve attention. But I think comparison asserts is common enough. Another case can be full code expanding:

>    let a = 10i64;
>    let b = 20i64;
>    let c = 21i64;
>    let d = 25i64;
>    assert!(get_segment(a, b).overlaps(get_segment(c, d)));
E    assert!(get_segment(10, 20).overlaps(get_segment(21, 25)))
E    assert!(Segment { min: 10, max: 20 }.overlaps(Segment { min: 21, max: 25 }))
E    assert!(false)

I don’t like special assertion macro, even exists assert_eq. Also I don’t like idea with excessive verbosity for tests definition. But to make test messages more clear look like good idea.

To make comparison assertion messages more clear I see three methods:

  1. add more macro - easy implement, probably less readable
  2. add explicit messages - too verbose
  3. use more complex assert! implementation - need implementation

#4

Why not aim higher? Have a look at https://code.google.com/p/spock/wiki/WhySpock to see how assert representation can be done. Granted, it uses Groovy which affords runtime reflective access on the code, but I bet something like this would be possible in Rust, too.