In Java how can I validate a thrown exception with JUnit?

前端 未结 10 1185
甜味超标
甜味超标 2020-11-30 05:47

When writing unit tests for a Java API there may be circumstances where you want to perform more detailed validation of an exception. I.e. more than is offered by the @t

相关标签:
10条回答
  • 2020-11-30 06:06

    @akuhn:

    Even without closures we can get a more readable solution (using catch-exception):

    import static com.googlecode.catchexception.CatchException.*;
    
    public void test() {
        ...
        ...
        catchException(nastyBoy).doNastyStuff();
        assertTrue(caughtException() instanceof WhateverException);
        assertEquals("Message", caughtException().getMessage());
    }
    
    0 讨论(0)
  • 2020-11-30 06:10

    Looking at the proposed answers, you can really feel the pain of not having closures in Java. IMHO, the most readable solution is ye good old try catch.

    @Test
    public void test() {
        ...
        ...
        try {
            ...
            fail("No exception caught :(");
        }
        catch (RuntimeException ex) {
            assertEquals(Whatever.class, ex.getCause().getClass());
            assertEquals("Message", ex.getMessage());
        }
    }
    
    0 讨论(0)
  • 2020-11-30 06:13

    In JUnit 4 it can be easily done using ExpectedException rule.

    Here is example from javadocs:

    // These tests all pass.
    public static class HasExpectedException {
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test
        public void throwsNothing() {
            // no exception expected, none thrown: passes.
        }
    
        @Test
        public void throwsNullPointerException() {
            thrown.expect(NullPointerException.class);
            throw new NullPointerException();
        }
    
        @Test
        public void throwsNullPointerExceptionWithMessage() {
            thrown.expect(NullPointerException.class);
            thrown.expectMessage("happened?");
            thrown.expectMessage(startsWith("What"));
            throw new NullPointerException("What happened?");
        }
    }
    
    0 讨论(0)
  • 2020-11-30 06:15

    Until this post I've done my exception validation by doing this:

    try {
        myObject.doThings();
        fail("Should've thrown SomeException!");
    } catch (SomeException e) {
        assertEquals("something", e.getSomething());
    }
    

    I spent a few moments thinking about the issue though and came up with the following (Java5, JUnit 3.x):

    // Functor interface for exception assertion.
    public interface AssertionContainer<T extends Throwable> {
        void invoke() throws T;
        void validate(T throwable);
        Class<T> getType();
    }
    
    // Actual assertion method.
    public <T extends Throwable> void assertThrowsException(AssertionContainer<T> functor) {
        try {
            functor.invoke();
            fail("Should've thrown "+functor.getType()+"!");
        } catch (Throwable exc) {
            assertSame("Thrown exception was of the wrong type! Expected "+functor.getClass()+", actual "+exc.getType(),
                       exc.getClass(), functor.getType());
            functor.validate((T) exc);
        }
    }
    
    // Example implementation for servlet I used to actually test this. It was an inner class, actually.
    AssertionContainer<ServletException> functor = new AssertionContainer<ServletException>() {
        public void invoke() throws ServletException {
            servlet.getRequiredParameter(request, "some_param");
        }
    
        public void validate(ServletException e) {
            assertEquals("Parameter \"some_param\" wasn't found!", e.getMessage());
        }
    
        public Class<ServletException> getType() {
            return ServletException.class;
        }
    }
    
    // And this is how it's used.
    assertThrowsException(functor);
    

    Looking at these two I can't decide which one I like more. I guess this is one of those issues where achieving a goal (in my case, the assertion method with functor parameter) isn't worth it in the long run since it's just a lot easier to do those 6+ of code to assert the try..catch block.

    Then again, maybe my 10 minute result of problem solving at friday evening just isn't the most intelligent way to do this.

    0 讨论(0)
  • 2020-11-30 06:23

    I made a helper similar to the other posted ones:

    public class ExpectExceptionsExecutor {
    
        private ExpectExceptionsExecutor() {
        }
    
        public static  void execute(ExpectExceptionsTemplate e) {
            Class<? extends Throwable> aClass = e.getExpectedException();
    
            try {
                Method method = ExpectExceptionsTemplate.class.getMethod("doInttemplate");
                method.invoke(e);
            } catch (NoSuchMethodException e1) {
    
    
                throw new RuntimeException();
            } catch (InvocationTargetException e1) {
    
    
                Throwable throwable = e1.getTargetException();
                if (!aClass.isAssignableFrom(throwable.getClass())) {
                    //  assert false
                    fail("Exception isn't the one expected");
                } else {
                    assertTrue("Exception captured ", true);
                    return;
                }
                ;
    
    
            } catch (IllegalAccessException e1) {
                throw new RuntimeException();
            }
    
            fail("No exception has been thrown");
        }
    
    
    }
    

    And the template the client should implement

    public interface ExpectExceptionsTemplate<T extends Throwable> {
    
    
        /**
         * Specify the type of exception that doInttemplate is expected to throw
         * @return
         */
        Class<T> getExpectedException();
    
    
        /**
         * Execute risky code inside this method
         * TODO specify expected exception using an annotation
         */
        public void doInttemplate();
    
    }
    

    And the client code would be something like this:

    @Test
        public void myTest() throws Exception {
            ExpectExceptionsExecutor.execute(new ExpectExceptionsTemplate() {
                @Override
                public Class getExpectedException() {
                    return IllegalArgumentException.class;
                }
    
                @Override
                public void doInttemplate() {
                    riskyMethod.doSomething(null);
                }
            });
         }
    

    It looks really verbose but if you use an IDE with good autocompletion you will only need to write the type of exception and the actual code under test. (the rest will be done by the IDE :D)

    0 讨论(0)
  • 2020-11-30 06:25

    i did something very simple

    testBla(){
        try {
            someFailingMethod()
            fail(); //method provided by junit
        } catch(Exception e) {
              //do nothing
        }
    }
    
    0 讨论(0)
提交回复
热议问题