'Assert expected exception with specific criteria in custom fields

I want to verify that an expected exception meets certain criteria. Take this as a starting point:

class MyException extends RuntimeException {
    int n;
    public MyException(String message, int n) {
        super(message);
        this.n = n;
    }
}

public class HowDoIDoThis {
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void test1() {
        thrown.expect(MyException.class);
        throw new MyException("x", 10);
    }
}

How do I assert, for example, that the thrown exception has n > 1 and message contains only lowercase letters? I was thinking of using thrown.expect(Matcher) but can't figure out how to get a Hamcrest matcher to check arbitrary fields of an object.



Solution 1:[1]

A consise and neat alternative way is to use AssertJ instead of the ExpectedException rule.

    assertThatThrownBy(() -> {
        throw new MyException("x", 10);
    })
        .matches(e -> e.getMessage().equals(e.getMessage().toLower()), "message is lowercase")
        .matches(e -> ((CustomException) e).n > 10, "n > 10");

Solution 2:[2]

There's also FeatureMatcher in Hamcrest that is great for creating matchers for nested "features" of objects. So in your example, you could structure it using FeatureMatcher in the following way (this is the pattern I tend to follow when creating matchers for nested fields):

public final class MyExceptionMatchers {

    public static Matcher<MyException> withNthat(Matcher<Integer> nMatcher) {
        return new FeatureMatcher<MyException, Integer>(nMatcher, "n", "n") {
              @Override
              protected Integer featureValueOf(MyException actual) {
                   return actual.n;
              }
        }
    };
}

And in your test:

import static x.y.z.MyExceptionMatchers.withNthat;
import static org.hamcrest.Matchers.greaterThan;

...

thrown.expect(withNThat(greaterThan(1)));

With this layout it's very easy to add more matchers for MyException and it feels like a more "canonical" approach to build composable matchers that allow you to construct the exact matcher you want for your test case.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Klitos Kyriacou
Solution 2 Taner