0

In my Android project I want to test, with the same @Test, a class which can throw several times the same exception with different messages. I want my test to pass for a given list of messages and to fail for others.

Making some research on Junit I tried to implement this using @Rule, expectMessage() and Hamcrest matcher.

My implementation is currently based on the "Custom matcher" described here.

@RunWith(AndroidJUnit4.class)
public class TestException extends ApplicationTestCase {

    @Rule public ExpectedException thrown= ExpectedException.none();

    public TestException(){
        super(AplicatyApplication.class);
    }

    @Test
    public void testException() throws Exception {
        thrown.expect(IndexOutOfBoundsException.class);
        thrown.expectMessage(new MatchesPattern("*"));
        Dummy.exec(0);
        // do more stuff here ...
        Dummy.exec(1);
        // ...
        Dummy.exec(2);
        // ...
        Dummy.exec(3); // I want my test to fail here
        // ...
    }

    class MatchesPattern extends TypeSafeMatcher<String> {
        private String pattern;

        public MatchesPattern(String pattern) {
            this.pattern = pattern;
        }

        @Override
        protected boolean matchesSafely(String item) {
            return item.matches(pattern)
                    &&
                    item.startsWith("My message")
                    && (
                        item.endsWith("1")
                        ||
                        item.endsWith("2")
                    );
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("matches pattern ").appendValue(pattern);
        }

        @Override
        protected void describeMismatchSafely(String item, Description mismatchDescription) {
            mismatchDescription.appendText("does not match");
        }
    }

    static class Dummy {
        static void exec(int i){
            if(i == 0)
                return;
            if(i == 1)
                throw new IndexOutOfBoundsException("My message1");
            if(i == 2)
                throw new IndexOutOfBoundsException("My message2");
            if(i == 3)
                throw new IndexOutOfBoundsException("My message3");
        }
    }
}

Running this test I can see that the matcher is called just once, executing Dummy.exec(1);.

The matchesSafely(String item) returns true and the the test ends with the status Passed. All this seems to be Okay, with my understanding of the @Rule. I was waiting an exception : I got it; I was waiting a given message : I got it.

I can not find a way to continue the execution of my test once the first exception has been thrown.

My questions are :

  • Is it possible to use @Rule to check more than one exception thrown into one tested method, or do I have to use the typical try/catch testing the exception message in every catch block?.
  • Is there another/more elegant way to test this type of concerns.
Community
  • 1
  • 1
Guillaume Barré
  • 4,168
  • 2
  • 27
  • 50

1 Answers1

1

I suggest to split the test method into multiple tests, one for each requirement.

@Test
public void testException_1() throws Exception {
    thrown.expect(IndexOutOfBoundsException.class);
    thrown.expectMessage("My message1");

    Dummy.exec(1);
}

In case it needs to be in one test method, I would build it with try-catch and ErrorCollector.

@Test
public void testException_1() throws Exception {
    try {
        Dummy.exec(1);
        fail();
    } catch (IndexOutOfBoundsException e) {
        errorCollector.checkThat(e.getMessage(), is("My message1"));
    }

    try {
        Dummy.exec(2);
        ...
    } ...
}

I would try to avoid to build a custom Matcher.

Roland Weisleder
  • 9,668
  • 7
  • 37
  • 59