How do you assert that a certain exception is thrown in JUnit 4 tests?

前端 未结 30 1933
忘掉有多难
忘掉有多难 2020-11-21 22:23

How can I use JUnit4 idiomatically to test that some code throws an exception?

While I can certainly do something like this:

@Test
public void testFo         


        
相关标签:
30条回答
  • 2020-11-21 22:52

    JUnit framework has assertThrows() method:

    ArithmeticException exception = assertThrows(ArithmeticException.class, () ->
        calculator.divide(1, 0));
    assertEquals("/ by zero", exception.getMessage());
    
    • for JUnit 5 it's in org.junit.jupiter.api.Assertions class;
    • for JUnit 4.13 it's in org.junit.Assert class;
    • for earlier versions of JUnit 4: just add reference on org.junit.jupiter:junit-jupiter-api to your project and you'll get perfectly well working version from JUnit 5.
    0 讨论(0)
  • 2020-11-21 22:53

    In JUnit 4 or later you can test the exceptions as follows

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


    this provides a lot of features which can be used to improve our JUnit tests.
    If you see the below example I am testing 3 things on the exception.

    1. The Type of exception thrown
    2. The exception Message
    3. The cause of the exception


    public class MyTest {
    
        @Rule
        public ExpectedException exceptions = ExpectedException.none();
    
        ClassUnderTest classUnderTest;
    
        @Before
        public void setUp() throws Exception {
            classUnderTest = new ClassUnderTest();
        }
    
        @Test
        public void testAppleisSweetAndRed() throws Exception {
    
            exceptions.expect(Exception.class);
            exceptions.expectMessage("this is the exception message");
            exceptions.expectCause(Matchers.<Throwable>equalTo(exceptionCause));
    
            classUnderTest.methodUnderTest("param1", "param2");
        }
    
    }
    
    0 讨论(0)
  • 2020-11-21 22:55

    Edit: Now that JUnit 5 and JUnit 4.13 have been released, the best option would be to use Assertions.assertThrows() (for JUnit 5) and Assert.assertThrows() (for JUnit 4.13+). See my other answer for details.

    If you haven't migrated to JUnit 5, but can use JUnit 4.7, you can use the ExpectedException Rule:

    public class FooTest {
      @Rule
      public final ExpectedException exception = ExpectedException.none();
    
      @Test
      public void doStuffThrowsIndexOutOfBoundsException() {
        Foo foo = new Foo();
    
        exception.expect(IndexOutOfBoundsException.class);
        foo.doStuff();
      }
    }
    

    This is much better than @Test(expected=IndexOutOfBoundsException.class) because the test will fail if IndexOutOfBoundsException is thrown before foo.doStuff()

    See this article for details.

    0 讨论(0)
  • 2020-11-21 22:55

    How about this: catch a very general exception, make sure it makes it out of the catch block, then assert that the class of the exception is what you expect it to be. This assert will fail if a) the exception is of the wrong type (eg. if you got a Null Pointer instead) and b) the exception wasn't ever thrown.

    public void testFooThrowsIndexOutOfBoundsException() {
      Throwable e = null;
    
      try {
        foo.doStuff();
      } catch (Throwable ex) {
        e = ex;
      }
    
      assertTrue(e instanceof IndexOutOfBoundsException);
    }
    
    0 讨论(0)
  • 2020-11-21 22:55

    Just make a Matcher that can be turned off and on, like this:

    public class ExceptionMatcher extends BaseMatcher<Throwable> {
        private boolean active = true;
        private Class<? extends Throwable> throwable;
    
        public ExceptionMatcher(Class<? extends Throwable> throwable) {
            this.throwable = throwable;
        }
    
        public void on() {
            this.active = true;
        }
    
        public void off() {
            this.active = false;
        }
    
        @Override
        public boolean matches(Object object) {
            return active && throwable.isAssignableFrom(object.getClass());
        }
    
        @Override
        public void describeTo(Description description) {
            description.appendText("not the covered exception type");
        }
    }
    

    To use it:

    add public ExpectedException exception = ExpectedException.none();, then:

    ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
    exception.expect(exMatch);
    someObject.somethingThatThrowsMyException();
    exMatch.off();
    
    0 讨论(0)
  • 2020-11-21 22:58

    With Java 8 you can create a method taking a code to check and expected exception as parameters:

    private void expectException(Runnable r, Class<?> clazz) { 
        try {
          r.run();
          fail("Expected: " + clazz.getSimpleName() + " but not thrown");
        } catch (Exception e) {
          if (!clazz.isInstance(e)) fail("Expected: " + clazz.getSimpleName() + " but " + e.getClass().getSimpleName() + " found", e);
        }
      }
    

    and then inside your test:

    expectException(() -> list.sublist(0, 2).get(2), IndexOutOfBoundsException.class);
    

    Benefits:

    • not relying on any library
    • localised check - more precise and allows to have multiple assertions like this within one test if needed
    • easy to use
    0 讨论(0)
提交回复
热议问题