8

I'm trying to verify that all my exceptions are correct. Because values are wrapped in CompletableFutures, the exception thrown is ExecutionException with cause being the exception that I would normally check. Quick example:

void foo() throws A {
  try {
    bar();
  } catch B b {
    throw new A(b);
  }
}

So foo() translates exception thrown by bar(), and all of that is done inside CompletableFutures and AsyncHandlers (I won't copy the whole code, it's just for reference)

In my unit tests I'm making bar() throw an exception and want to check that it's translated correctly when calling foo():

Throwable b = B("bleh");
when(mock.bar()).thenThrow(b);
ExpectedException thrown = ExpectedException.none();
thrown.expect(ExecutionException.class);
thrown.expectCause(Matchers.allOf(
                instanceOf(A.class),
                having(on(A.class).getMessage(),
                        CoreMatchers.is("some message here")),
        ));

So far so good, but I also want to verify that cause of exception A is exception B and having(on(A.class).getCause(), CoreMatchers.is(b)) causes CodeGenerationException --> StackOverflowError

TL;DR: How do I get cause of cause of expected exception?

Amir
  • 717
  • 2
  • 9
  • 21
  • Maybe this question is what you want (I'm not sure): https://stackoverflow.com/questions/871216/junit-possible-to-expect-a-wrapped-exception/20759785#20759785 –  Nov 25 '16 at 18:29
  • @RC. Almost, but I must go one level deeper and get the next cause :) – Amir Nov 25 '16 at 21:55
  • 1
    You should be able to adapt: https://stackoverflow.com/a/6528640/180100 –  Nov 26 '16 at 09:24

3 Answers3

4

Maybe you should try with the simple hasProperty Matcher, in order to isolate the problem:

thrown.expectCause(allOf(
                    instanceOf(A.class),
                    hasProperty("message", is("some message here")),
        ));
Ruben
  • 3,986
  • 1
  • 21
  • 34
0

This is an example I use to test only the causal class chain. References :


import static org.hamcrest.Matchers.contains;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

public class CausalClassChainTest {

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

    @Test
    public void test() throws Exception {
        expectedException.expect(IOException.class);
        expectedException.expectCause(new CausalClassChainMather(Exception.class, RuntimeException.class));

        throw new IOException(new Exception(new RuntimeException()));
    }

    private static class CausalClassChainMather extends TypeSafeMatcher<Throwable> {

        private final Class<? extends Throwable>[] expectedClasses;
        private List<Class<? extends Throwable>> causualClasses;
        private Matcher<Iterable<? extends Class<? extends Throwable>>> matcher;

        public CausalClassChainMather(Class<? extends Throwable>... classes) {
            this.expectedClasses = classes;
        }

        @Override
        public void describeTo(Description description) {
            // copy of MatcherAssert.assertThat()
            description.appendText("")
                    .appendText("\nExpected: ")
                    .appendDescriptionOf(matcher)
                    .appendText("\n     but: ");
            matcher.describeMismatch(causualClasses, description);
        }

        @Override
        protected boolean matchesSafely(Throwable item) {

            List<Class<? extends Throwable>> causes = new ArrayList<Class<? extends Throwable>>();
            while (item != null) {
                causes.add(item.getClass());
                item = item.getCause();
            }
            causualClasses = Collections.unmodifiableList(causes);

            // ordered test
            matcher = contains(expectedClasses);
            return matcher.matches(causualClasses);
        }
    }

}
0

Try sth like, to verify the cause of the cause:

    thrown.expectCause(allOf(
            isA(org.apache.kafka.common.errors.SerializationException.class),
            hasProperty("message", containsString("Error deserializing Avro message for id")),
            hasProperty("cause", allOf(
                    isA(org.apache.avro.AvroTypeException.class),
                    hasProperty("message", containsString("missing required field blabla"))
                    ))
            ));
Vassilis
  • 914
  • 8
  • 23