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

前端 未结 30 1939
忘掉有多难
忘掉有多难 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:59

    You can also do this:

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            assert false;
        } catch (IndexOutOfBoundsException e) {
            assert true;
        }
    }
    
    0 讨论(0)
  • 2020-11-21 22:59

    The most flexible and elegant answer for Junit 4 I found in the Mkyong blog. It has the flexibility of the try/catch using the @Rule annotation. I like this approach because you can read specific attributes of a customized exception.

    package com.mkyong;
    
    import com.mkyong.examples.CustomerService;
    import com.mkyong.examples.exception.NameNotFoundException;
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.rules.ExpectedException;
    
    import static org.hamcrest.CoreMatchers.containsString;
    import static org.hamcrest.CoreMatchers.is;
    import static org.hamcrest.Matchers.hasProperty;
    
    public class Exception3Test {
    
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test
        public void testNameNotFoundException() throws NameNotFoundException {
    
            //test specific type of exception
            thrown.expect(NameNotFoundException.class);
    
            //test message
            thrown.expectMessage(is("Name is empty!"));
    
            //test detail
            thrown.expect(hasProperty("errCode"));  //make sure getters n setters are defined.
            thrown.expect(hasProperty("errCode", is(666)));
    
            CustomerService cust = new CustomerService();
            cust.findByName("");
    
        }
    
    }
    
    0 讨论(0)
  • 2020-11-21 22:59

    In my case I always get RuntimeException from db, but messages differ. And exception need to be handled respectively. Here is how I tested it:

    @Test
    public void testThrowsExceptionWhenWrongSku() {
    
        // Given
        String articleSimpleSku = "999-999";
        int amountOfTransactions = 1;
        Exception exception = null;
    
        // When
        try {
            createNInboundTransactionsForSku(amountOfTransactions, articleSimpleSku);
        } catch (RuntimeException e) {
            exception = e;
        }
    
        // Then
        shouldValidateThrowsExceptionWithMessage(exception, MESSAGE_NON_EXISTENT_SKU);
    }
    
    private void shouldValidateThrowsExceptionWithMessage(final Exception e, final String message) {
        assertNotNull(e);
        assertTrue(e.getMessage().contains(message));
    }
    
    0 讨论(0)
  • 2020-11-21 23:00
        @Test(expectedException=IndexOutOfBoundsException.class) 
        public void  testFooThrowsIndexOutOfBoundsException() throws Exception {
             doThrow(IndexOutOfBoundsException.class).when(foo).doStuff();  
             try {
                 foo.doStuff(); 
                } catch (IndexOutOfBoundsException e) {
                           assertEquals(IndexOutOfBoundsException .class, ex.getCause().getClass());
                          throw e;
    
                   }
    
        }
    

    Here is another way to check method thrown correct exception or not.

    0 讨论(0)
  • 2020-11-21 23:01

    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 the Junit 5 User Guide.

    Here is an example that verifies an exception is thrown, and uses Truth to make assertions on the exception message:

    public class FooTest {
      @Test
      public void doStuffThrowsIndexOutOfBoundsException() {
        Foo foo = new Foo();
    
        IndexOutOfBoundsException e = assertThrows(
            IndexOutOfBoundsException.class, foo::doStuff);
    
        assertThat(e).hasMessageThat().contains("woops!");
      }
    }
    

    The advantages over the approaches in the other answers are:

    1. Built into JUnit
    2. You get a useful exception message if the code in the lambda doesn't throw an exception, and a stacktrace if it throws a different exception
    3. Concise
    4. Allows your tests to follow Arrange-Act-Assert
    5. You can precisely indicate what code you are expecting to throw the exception
    6. You don't need to list the expected exception in the throws clause
    7. You can use the assertion framework of your choice to make assertions about the caught exception

    A similar method will be added to org.junit Assert in JUnit 4.13.

    0 讨论(0)
  • 2020-11-21 23:01

    IMHO, the best way to check for exceptions in JUnit is the try/catch/fail/assert pattern:

    // this try block should be as small as possible,
    // as you want to make sure you only catch exceptions from your code
    try {
        sut.doThing();
        fail(); // fail if this does not throw any exception
    } catch(MyException e) { // only catch the exception you expect,
                             // otherwise you may catch an exception for a dependency unexpectedly
        // a strong assertion on the message, 
        // in case the exception comes from anywhere an unexpected line of code,
        // especially important if your checking IllegalArgumentExceptions
        assertEquals("the message I get", e.getMessage()); 
    }
    

    The assertTrue might be a bit strong for some people, so assertThat(e.getMessage(), containsString("the message"); might be preferable.

    0 讨论(0)
提交回复
热议问题