JUnit: Possible to 'expect' a wrapped exception?

后端 未结 7 1799
花落未央
花落未央 2020-12-05 12:43

I know that one can define an \'expected\' exception in JUnit, doing:

@Test(expect=MyException.class)
public void someMethod() { ... }


        
相关标签:
7条回答
  • As of JUnit 4.11 you can use the ExpectedException rule's expectCause() method:

    import static org.hamcrest.CoreMatchers.*;
    
    // ...
    
    @Rule
    public ExpectedException expectedException = ExpectedException.none();
    
    @Test
    public void throwsNestedException() throws Exception {
        expectedException.expectCause(isA(SomeNestedException.class));
    
        throw new ParentException("foo", new SomeNestedException("bar"));
    }
    
    0 讨论(0)
  • 2020-12-05 13:15

    I wrote a little JUnit extension for that purpose. A static helper function takes a function body and an array of expected exceptions:

    import static org.junit.Assert.assertTrue;
    import static org.junit.Assert.fail;
    
    import java.util.Arrays;
    
    public class AssertExt {
        public static interface Runnable {
            void run() throws Exception;
        }
    
        public static void assertExpectedExceptionCause( Runnable runnable, @SuppressWarnings("unchecked") Class[] expectedExceptions ) {
            boolean thrown = false;
            try {
                runnable.run();
            } catch( Throwable throwable ) {
                final Throwable cause = throwable.getCause();
                if( null != cause ) {
                    assertTrue( Arrays.asList( expectedExceptions ).contains( cause.getClass() ) );
                    thrown = true;
                }
            }
            if( !thrown ) {
                fail( "Expected exception not thrown or thrown exception had no cause!" );
            }
        }
    }
    

    You can now check for expected nested exceptions like so:

    import static AssertExt.assertExpectedExceptionCause;
    
    import org.junit.Test;
    
    public class TestExample {
        @Test
        public void testExpectedExceptionCauses() {
            assertExpectedExceptionCause( new AssertExt.Runnable(){
                public void run() throws Exception {
                    throw new Exception( new NullPointerException() );
                }
            }, new Class[]{ NullPointerException.class } );
        }
    }
    

    This saves you writing the same boiler plate code again and again.

    0 讨论(0)
  • 2020-12-05 13:18

    You could always do it manually:

    @Test
    public void someMethod() {
        try{
            ... all your code
        } catch (Exception e){
            // check your nested clauses
            if(e.getCause() instanceof FooException){
                // pass
            } else {
                Assert.fail("unexpected exception");
            }
        }
    
    0 讨论(0)
  • 2020-12-05 13:20

    The most concise syntax is provided by catch-exception:

    import static com.googlecode.catchexception.CatchException.*;
    
    catchException(myObj).doSomethingNasty();
    assertTrue(caughtException().getCause() instanceof MyException);
    
    0 讨论(0)
  • 2020-12-05 13:23

    You could wrap the testing code in a try / catch block, catch the thrown exception, check the internal cause, log / assert / whatever, and then rethrow the exception (if desired).

    0 讨论(0)
  • 2020-12-05 13:34

    You could create a Matcher for exceptions. This works even when you are using another test runner like Arquillian's @RunWith(Arquillian.class) so you can't use the @RunWith(ExtendedTestRunner.class) approach suggested above.

    Here's a simple example:

    public class ExceptionMatcher extends BaseMatcher<Object> {
        private Class<? extends Throwable>[] classes;
    
        // @SafeVarargs // <-- Suppress warning in Java 7. This usage is safe.
        public ExceptionMatcher(Class<? extends Throwable>... classes) {
            this.classes = classes;
        }
    
        @Override
        public boolean matches(Object item) {
            for (Class<? extends Throwable> klass : classes) {
                if (! klass.isInstance(item)) {
                    return false;
                }   
    
                item = ((Throwable) item).getCause();
            }   
    
            return true;
        }   
    
        @Override
        public void describeTo(Description descr) {
            descr.appendText("unexpected exception");
        }
    }
    

    Then use it with @Rule and ExpectedException like this:

    @Rule
    public ExpectedException thrown = ExpectedException.none();
    
    @Test
    public void testSomething() {
        thrown.expect(new ExceptionMatcher(IllegalArgumentException.class, IllegalStateException.class));
    
        throw new IllegalArgumentException("foo", new IllegalStateException("bar"));
    }
    

    Added by Craig Ringer in 2012 edit: An enhanced and more reliable version:

    • Basic usage unchanged from above
    • Can pass optional 1st argument boolean rethrow to throw unmatched exception. That preserves the stack trace of the nested exceptions for easier debugging.
    • Uses Apache Commons Lang ExceptionUtils to handle cause loops and to handle non-standard exception nesting used by some common exception classes.
    • Self-describe includes accepted exceptions
    • Self-describe on failure includes a the cause stack of the exception encountered
    • Handle Java 7 warning. Remove the @SaveVarargs on older versions.

    Full code:

    import org.apache.commons.lang3.exception.ExceptionUtils;
    import org.hamcrest.BaseMatcher;
    import org.hamcrest.Description;
    
    
    public class ExceptionMatcher extends BaseMatcher<Object> {
        private Class<? extends Throwable>[] acceptedClasses;
    
        private Throwable[] nestedExceptions;
        private final boolean rethrow;
    
        @SafeVarargs
        public ExceptionMatcher(Class<? extends Throwable>... classes) {
            this(false, classes);
        }
    
        @SafeVarargs
        public ExceptionMatcher(boolean rethrow, Class<? extends Throwable>... classes) {
            this.rethrow = rethrow;
            this.acceptedClasses = classes;
        }
    
        @Override
        public boolean matches(Object item) {
            nestedExceptions = ExceptionUtils.getThrowables((Throwable)item);
            for (Class<? extends Throwable> acceptedClass : acceptedClasses) {
                for (Throwable nestedException : nestedExceptions) {
                    if (acceptedClass.isInstance(nestedException)) {
                        return true;
                    }
                }
            }
            if (rethrow) {
                throw new AssertionError(buildDescription(), (Throwable)item);
            }
            return false;
        }
    
        private String buildDescription() {
            StringBuilder sb = new StringBuilder();
            sb.append("Unexpected exception. Acceptable (possibly nested) exceptions are:");
            for (Class<? extends Throwable> klass : acceptedClasses) {
                sb.append("\n  ");
                sb.append(klass.toString());
            }
            if (nestedExceptions != null) {
                sb.append("\nNested exceptions found were:");
                for (Throwable nestedException : nestedExceptions) {
                    sb.append("\n  ");
                    sb.append(nestedException.getClass().toString());
                }
            }
            return sb.toString();
        }
    
        @Override
        public void describeTo(Description description) {
            description.appendText(buildDescription());
        }
    
    }
    

    Typical output:

    java.lang.AssertionError:  Expected: Unexpected exception. Acceptable (possibly nested) exceptions are:
       class some.application.Exception
    Nested exceptions found were:
       class javax.ejb.EJBTransactionRolledbackException
       class javax.persistence.NoResultException
         got: <javax.ejb.EJBTransactionRolledbackException: getSingleResult() did not retrieve any entities.>
    
    0 讨论(0)
提交回复
热议问题