首页 文章

你怎么断言在JUnit 4测试中抛出某个异常?

提问于
浏览
1718

如何以惯用方式使用JUnit4来测试某些代码是否会抛出异常?

虽然我当然可以这样做:

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;

  try {
    foo.doStuff();
  } catch (IndexOutOfBoundsException e) {
    thrown = true;
  }

  assertTrue(thrown);
}

我记得有一个注释或者一个Assert.xyz或者其他东西,对于这些类型的情况来说,远不如KUndgy和JUnit的精神 .

30 回答

  • 0

    JUnit 4支持这个:

    @Test(expected = IndexOutOfBoundsException.class)
    public void testIndexOutOfBoundsException() {
        ArrayList emptyList = new ArrayList();
        Object o = emptyList.get(0);
    }
    

    参考:https://junit.org/junit4/faq.html#atests_7

  • 4

    你也可以这样做:

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            assert false;
        } catch (IndexOutOfBoundsException e) {
            assert true;
        }
    }
    
  • 11

    使用Java 8,您可以创建一个方法,将代码检查并将预期的异常作为参数:

    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);
        }
      }
    

    然后在你的测试中:

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

    优点:

    • 不依赖任何库

    • 本地化检查 - 更精确,并允许在一个测试中有多个断言,如果需要的话

    • 易于使用

  • 33

    使用Java8的Junit4解决方案是使用此功能:

    public Throwable assertThrows(Class<? extends Throwable> expectedException, java.util.concurrent.Callable<?> funky) {
        try {
            funky.call();
        } catch (Throwable e) {
            if (expectedException.isInstance(e)) {
                return e;
            }
            throw new AssertionError(
                    String.format("Expected [%s] to be thrown, but was [%s]", expectedException, e));
        }
        throw new AssertionError(
                String.format("Expected [%s] to be thrown, but nothing was thrown.", expectedException));
    }
    

    用法是:

    assertThrows(ValidationException.class,
                () -> finalObject.checkSomething(null));
    

    请注意,唯一的限制是在lambda表达式中使用 final 对象引用 . 此解决方案允许继续测试断言,而不是期望使用 @Test(expected = IndexOutOfBoundsException.class) 解决方案在方法级别上可提升 .

  • 19

    现在JUnit 5已经发布,最好的选择是使用 Assertions.assertThrows() (参见Junit 5 User Guide) .

    下面是一个验证抛出异常的示例,并使用Truth对异常消息进行断言:

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

    其他答案中的方法优势是:

    • 内置于JUnit

    • 如果lambda中的代码没有抛出异常,你会得到一个有用的异常消息,如果它抛出一个不同的异常,你会得到一个栈跟踪

    • 简洁

    • 允许您的测试遵循Arrange-Act-Assert

    • 您可以准确地指出您希望抛出异常的代码

    • 您无需在 throws 子句中列出预期的异常

    • 您可以使用您选择的断言框架来对捕获的异常进行断言

    类似的方法将添加到JUnit 4.13中的 org.junit Assert .

  • 6

    我建议库 assertj-core 在junit测试中处理异常

    在java 8中,像这样:

    //given
    
    //when
    Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));
    
    //then
    AnyException anyException = (AnyException) throwable;
    assertThat(anyException.getMessage()).isEqualTo("........");
    assertThat(exception.getCode()).isEqualTo(".......);
    
  • 195

    我使用Java 8 lambdas的解决方案:

    public static <T extends Throwable> T assertThrows(Class<T> expected, ThrowingRunnable action) throws Throwable {
        try {
            action.run();
            Assert.fail("Did not throw expected " + expected.getSimpleName());
            return null; // never actually
        } catch (Throwable actual) {
            if (!expected.isAssignableFrom(actual.getClass())) { // runtime '!(actual instanceof expected)'
                System.err.println("Threw " + actual.getClass().getSimpleName() 
                                   + ", which is not a subtype of expected " 
                                   + expected.getSimpleName());
                throw actual; // throw the unexpected Throwable for maximum transparency
            } else {
                return (T) actual; // return the expected Throwable for further examination
            }
        }
    }
    

    您必须定义一个FunctionalInterface,因为 Runnable 没有声明所需的 throws .

    @FunctionalInterface
    public interface ThrowingRunnable {
        void run() throws Throwable;
    }
    

    该方法可以如下使用:

    class CustomException extends Exception {
        public final String message;
        public CustomException(final String message) { this.message = message;}
    }
    CustomException e = assertThrows(CustomException.class, () -> {
        throw new CustomException("Lorem Ipsum");
    });
    assertEquals("Lorem Ipsum", e.message);
    
  • 423

    Update: JUnit5对异常测试有一个改进: assertThrows .

    以下示例来自:Junit 5 User Guide

    @Test
    void exceptionTesting() {
        Throwable exception = assertThrows(IllegalArgumentException.class, () -> 
        {
            throw new IllegalArgumentException("a message");
        });
        assertEquals("a message", exception.getMessage());
    }
    

    Original answer using JUnit 4.

    有几种方法可以测试抛出异常 . 我在帖子中也讨论过以下选项How to write great unit tests with JUnit

    设置 expected 参数 @Test(expected = FileNotFoundException.class) .

    @Test(expected = FileNotFoundException.class) 
    public void testReadFile() { 
        myClass.readFile("test.txt");
    }
    

    使用 try catch

    public void testReadFile() { 
        try {
            myClass.readFile("test.txt");
            fail("Expected a FileNotFoundException to be thrown");
        } catch (FileNotFoundException e) {
            assertThat(e.getMessage(), is("The file test.txt does not exist!"));
        }
    
    }
    

    使用 ExpectedException 规则进行测试 .

    @Rule
    public ExpectedException thrown = ExpectedException.none();
    
    @Test
    public void testReadFile() throws FileNotFoundException {
    
        thrown.expect(FileNotFoundException.class);
        thrown.expectMessage(startsWith("The file test.txt"));
        myClass.readFile("test.txt");
    }
    

    您可以在JUnit4 wiki for Exception testingbad.robot - Expecting Exceptions JUnit Rule中阅读有关异常测试的更多信息 .

  • 5

    在必须返回异常的方法之后,我们可以使用断言失败:

    try{
       methodThatThrowMyException();
       Assert.fail("MyException is not thrown !");
    } catch (final Exception exception) {
       // Verify if the thrown exception is instance of MyException, otherwise throws an assert failure
       assertTrue(exception instanceof MyException, "An exception other than MyException is thrown !");
       // In case of verifying the error message
       MyException myException = (MyException) exception;
       assertEquals("EXPECTED ERROR MESSAGE", myException.getMessage());
    }
    
  • 2062

    例如,您想为下面提到的代码片段编写Junit

    public int divideByZeroDemo(int a,int b){
    
        return a/b;
    }
    
    public void exceptionWithMessage(String [] arr){
    
        throw new ArrayIndexOutOfBoundsException("Array is out of bound");
    }
    

    上面的代码是测试可能发生的一些未知异常,下面的代码是使用自定义消息声明一些异常 .

    @Rule
    public ExpectedException exception=ExpectedException.none();
    
    private Demo demo;
    @Before
    public void setup(){
    
        demo=new Demo();
    }
    @Test(expected=ArithmeticException.class)
    public void testIfItThrowsAnyException() {
    
        demo.divideByZeroDemo(5, 0);
    
    }
    
    @Test
    public void testExceptionWithMessage(){
    
    
        exception.expectMessage("Array is out of bound");
        exception.expect(ArrayIndexOutOfBoundsException.class);
        demo.exceptionWithMessage(new String[]{"This","is","a","demo"});
    }
    
  • 0

    为了解决同样的问题,我确实设置了一个小项目:http://code.google.com/p/catch-exception/

    使用这个小助手你会写

    verifyException(foo, IndexOutOfBoundsException.class).doStuff();
    

    这比JUnit 4.7的ExpectedException规则简洁得多 . 与skaffman提供的解决方案相比,您可以指定您期望异常的代码行 . 我希望这有帮助 .

  • 12

    JUnit 5解决方案

    @Test
    void testFooThrowsIndexOutOfBoundsException() {    
      Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );
    
      assertEquals( "some message", exception.getMessage() );
    }
    

    有关JUnit 5的更多信息,请访问http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions

  • 4

    编写测试用例有两种方法

    • 使用方法抛出的异常对测试进行批注 . 像这样的东西 @Test(expected = IndexOutOfBoundsException.class)

    • 您可以使用try catch块在测试类中捕获异常,并对从测试类中的方法抛出的消息进行断言 .

    try{
    }
    catch(exception to be thrown from method e)
    {
         assertEquals("message", e.getmessage());
    }
    

    我希望这能回答你的问题快乐学习......

  • 8

    BDD样式解决方案:JUnit 4 Catch Exception AssertJ

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
    
        when(foo).doStuff();
    
        then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);
    
    }
    

    源代码

    依赖关系

    eu.codearte.catch-exception:catch-exception:1.3.3
    
  • 9

    怎么样:捕获一个非常一般的异常,确保它使它脱离catch块,然后断言异常的类是你期望的那样 . 如果a)异常是错误的类型(例如,如果你有一个Null指针)而b)没有抛出异常,那么这个断言将失败 .

    public void testFooThrowsIndexOutOfBoundsException() {
      Throwable e = null;
    
      try {
        foo.doStuff();
      } catch (Throwable ex) {
        e = ex;
      }
    
      assertTrue(e instanceof IndexOutOfBoundsException);
    }
    
  • 7

    如前所述,有许多方法可以处理JUnit中的异常 . 但是对于Java 8,还有另外一个:使用Lambda Expressions . 使用Lambda Expressions,我们可以实现如下语法:

    @Test
    public void verifiesTypeAndMessage() {
        assertThrown(new DummyService()::someMethod)
                .isInstanceOf(RuntimeException.class)
                .hasMessage("Runtime exception occurred")
                .hasMessageStartingWith("Runtime")
                .hasMessageEndingWith("occurred")
                .hasMessageContaining("exception")
                .hasNoCause();
    }
    

    assertThrown接受一个功能接口,其实例可以使用lambda表达式,方法引用或构造函数引用创建 . assertThrown接受该接口将期望并准备好处理异常 .

    这是相对简单但功能强大的技术 .

    看看这篇描述这种技术的博客文章:http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

    源代码可以在这里找到:https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8

    披露:我是博客和项目的作者 .

  • 24

    我在Mkyong blog找到的Junit 4最灵活,最优雅的答案 . 它具有使用 @Rule 注释的 try/catch 的灵活性 . 我喜欢这种方法,因为您可以读取自定义异常的特定属性 .

    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("");
    
        }
    
    }
    
  • 5

    除了NamShubWriter所说的,请确保:

    • ExpectedException实例是 publicRelated Question

    • ExpectedException isn't 实例化为@Before方法 . 这个post清楚地解释了JUnit执行顺序的所有复杂性 .

    not 这样做:

    @Rule    
    public ExpectedException expectedException;
    
    @Before
    public void setup()
    {
        expectedException = ExpectedException.none();
    }
    

    最后,this博客文章清楚地说明了如何断言抛出某个异常 .

  • 31

    在JUnit 4或更高版本中,您可以按如下方式测试异常

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

    这提供了许多可用于改进JUnit测试的功能 .
    如果您看到以下示例,我将在异常上测试3件事 .

    • 抛出的异常类型

    • 异常消息

    • 异常的原因

    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

    小心使用预期的异常,因为它只声明 method 抛出了该异常,而不是测试中的 particular line of code .

    我倾向于使用它来测试参数验证,因为这些方法通常非常简单,但更复杂的测试可能更适合:

    try {
        methodThatShouldThrow();
        fail( "My method didn't throw when I expected it to" );
    } catch (MyException expectedException) {
    }
    

    申请判决 .

  • 0

    只需制作一个可以关闭和打开的匹配器,如下所示:

    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");
        }
    }
    

    要使用它:

    添加 public ExpectedException exception = ExpectedException.none(); ,然后:

    ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
    exception.expect(exMatch);
    someObject.somethingThatThrowsMyException();
    exMatch.off();
    
  • 92

    Edit 现在JUnit5已经发布,最好的选择是使用 Assertions.assertThrows() (参见my other answer) .

    如果您尚未迁移到JUnit 5,但可以使用JUnit 4.7,则可以使用ExpectedException规则:

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

    这比 @Test(expected=IndexOutOfBoundsException.class) 要好得多,因为如果在 foo.doStuff() 之前抛出 IndexOutOfBoundsException ,测试将会失败

    有关详细信息,请参阅this article

  • 67

    tl;dr

    • pre-JDK8:我会推荐旧的 try - catch 块 .

    • post-JDK8:使用AssertJ或自定义lambdas来断言异常行为 .

    无论是Junit 4还是JUnit 5 .

    the long story

    可以自己编写一个自己动手 try - catch 块或使用JUnit工具( @Test(expected = ...)@Rule ExpectedException JUnit规则功能) .

    但是这些方式并不那么优雅,并且不能很好地与其他工具混合使用 .

    • try - catch 块您必须围绕测试行为编写块,并在catch块中写入断言,这可能没什么问题,但很多人发现这种样式会中断测试的读取流程 . 你还需要在 try 块的末尾写一个 Assert.fail ,否则测试可能会错过断言的一面; PMD,findbugs或Sonar将发现此类问题 .

    • @Test(expected = ...) 功能很有趣,因为您可以编写更少的代码,然后编写此测试可能不太容易出现编码错误 . _75885这种做法缺乏一些领域 .

    • 如果测试需要检查异常上的其他内容,例如原因或消息(良好的异常消息非常重要,具有精确的异常类型可能还不够) .

    • 此外,由于期望放在方法中,取决于测试代码的编写方式,然后测试代码的错误部分可能抛出异常,导致误报测试,我不确定PMD,findbugs或Sonar将会提供有关此类代码的提示 .

    @Test(expected = WantedException.class)
    public void call2_should_throw_a_WantedException__not_call1() {
        // init tested
        tested.call1(); // may throw a WantedException
    
        // call to be actually tested
        tested.call2(); // the call that is supposed to raise an exception
    }
    
    • ExpectedException 规则也试图解决之前的警告,但使用期望风格时使用感觉有点尴尬,EasyMock用户非常清楚这种风格 . 对某些人来说可能很方便,但如果你遵循行为驱动开发(BDD)或安排行为断言(AAA)原则, ExpectedException 规则将不适合那些写作风格 . 除此之外,它可能会遇到与 @Test 方式相同的问题,具体取决于您期望的位置 .
    @Rule ExpectedException thrown = ExpectedException.none()
    
    @Test
    public void call2_should_throw_a_WantedException__not_call1() {
        // expectations
        thrown.expect(WantedException.class);
        thrown.expectMessage("boom");
    
        // init tested
        tested.call1(); // may throw a WantedException
    
        // call to be actually tested
        tested.call2(); // the call that is supposed to raise an exception
    }
    

    即使预期的异常放在测试语句之前,如果测试遵循BDD或AAA,它也会破坏您的读取流程 .

    另请参阅 ExpectedException 作者JUnit上的comment问题 .

    所以这些上面的选项都有他们的注意事项,显然不能免于编码器错误 .

    • 有's a project I became aware after creating this answer that looks promising, it' s catch-exception .

    正如项目描述所说的那样,它让编码器用流畅的代码行编写代码来捕获异常并为以后的断言提供此异常 . 您可以使用任何断言库,如HamcrestAssertJ .

    从主页获取的一个快速示例:

    // given: an empty list
    List myList = new ArrayList();
    
    // when: we try to get the first element of the list
    when(myList).get(1);
    
    // then: we expect an IndexOutOfBoundsException
    then(caughtException())
            .isInstanceOf(IndexOutOfBoundsException.class)
            .hasMessage("Index: 1, Size: 0") 
            .hasNoCause();
    

    正如您所看到的,代码非常简单,您可以在特定行上捕获异常, then API是将使用AssertJ API的别名(类似于使用 assertThat(ex).hasNoCause()... ) . 在某些时候,该项目依赖于FEST-Assert的AssertJ的祖先 . EDIT: 该项目似乎正在酝酿Java 8 Lambdas支持 .

    目前这个库有两个缺点:

    • 在撰写本文时,值得注意的是,该库基于Mockito 1.x,因为它创建了一个模拟场景背后的测试对象 . 由于Mockito仍未更新 this library cannot work with final classes or final methods . 即使它是基于当前版本的mockito 2,这也需要声明一个全局模拟制作者( inline-mock-maker ),这可能不是你想要的东西,因为这个模拟器具有与常规模拟器不同的缺点 .

    • 它需要另一个测试依赖项 .

    一旦库支持lambdas,这些问题将不适用,但AssertJ工具集将复制该功能 .

    Taking all into account if you don't want to use the catch-exception tool, I will recommend the old good way of the try-catch block, at least up to the JDK7. And for JDK 8 users you might prefer to use AssertJ as it offers may more than just asserting exceptions.

    • 使用JDK8,lambdas进入测试场景,并且它们已被证明是一种断言异常行为的有趣方式 . AssertJ已经更新,提供了一个很好的流畅API来断言异常行为 .

    并使用AssertJ进行示例测试:

    @Test
    public void test_exception_approach_1() {
        ...
        assertThatExceptionOfType(IOException.class)
                .isThrownBy(() -> someBadIOOperation())
                .withMessage("boom!"); 
    }
    
    @Test
    public void test_exception_approach_2() {
        ...
        assertThatThrownBy(() -> someBadIOOperation())
                .isInstanceOf(Exception.class)
                .hasMessageContaining("boom");
    }
    
    @Test
    public void test_exception_approach_3() {
        ...
        // when
        Throwable thrown = catchThrowable(() -> someBadIOOperation());
    
        // then
        assertThat(thrown).isInstanceOf(Exception.class)
                          .hasMessageContaining("boom");
    }
    
    • 几乎完全重写了JUnit 5,断言已经improved,它们可能被证明是一种开箱即用的方式来断言正确的异常 . 但真的断言API仍然有点差,没有任何东西在外面assertThrows .
    @Test
    @DisplayName("throws EmptyStackException when peeked")
    void throwsExceptionWhenPeeked() {
        Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek());
    
        Assertions.assertEquals("...", t.getMessage());
    }
    

    正如您所注意到的, assertEquals 仍在返回 void ,因此不允许链接断言,如AssertJ .

    此外,如果你记得与 MatcherAssert 的名字冲突,请准备好与 Assertions 相同的冲突 .

    我想得出结论,今天(2017-03-03) AssertJ 的易用性,可发现的API,快速的开发速度以及作为事实上的测试依赖是JDK8的最佳解决方案,无论测试框架如何(JUnit或之前的JDK应该依赖于 try-catch 块,即使它们感觉很笨拙 .

    此答案已从another question复制,但没有相同的可见性,我是同一作者 .

  • 1

    JUnit内置了对此的支持,"expected" attribute

  • 30

    使用AssertJ断言,可以与JUnit一起使用:

    import static org.assertj.core.api.Assertions.*;
    
    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
      Foo foo = new Foo();
    
      assertThatThrownBy(() -> foo.doStuff())
            .isInstanceOf(IndexOutOfBoundsException.class);
    }
    

    它比 @Test(expected=IndexOutOfBoundsException.class) 更好,因为它保证了测试中的预期行引发异常,并让您检查有关异常的更多详细信息,例如消息,更容易:

    assertThatThrownBy(() ->
           {
             throw new Exception("boom!");
           })
        .isInstanceOf(Exception.class)
        .hasMessageContaining("boom");
    

    Maven/Gradle instructions here.

  • 8

    在我的情况下,我总是从db获得RuntimeException,但消息不同 . 并且需要分别处理异常 . 以下是我测试它的方式:

    @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

    在junit中,有四种方法可以测试异常 .

    • for junit4.x,使用Test annonation的可选'expected'属性
    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
    
    • for junit4.x,使用ExpectedException规则
    public class XxxTest {
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test
        public void testFooThrowsIndexOutOfBoundsException() {
            thrown.expect(IndexOutOfBoundsException.class)
            //you can test the exception message like
            thrown.expectMessage("expected messages");
            foo.doStuff();
        }
    }
    
    • 你也可以使用在junit 3框架下广泛使用的经典try / catch方式
    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            fail("expected exception was not occured.");
        } catch(IndexOutOfBoundsException e) {
            //if execution reaches here, 
            //it indicates this exception was occured.
            //so we need not handle it.
        }
    }
    
    • 最后,对于junit5.x,您还可以使用assertThrows,如下所示
    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
        assertEquals("expected messages", exception.getMessage());
    }
    
    • 所以

    • 当您只想测试异常类型时使用第一种方式

    • 当您想要进一步测试异常消息时,将使用其他三种方法

    • 如果你使用junit 3,那么第三个是首选

    • 如果你喜欢junit 5,那么你应该喜欢第4个

    • 了解更多信息,您可以阅读this documentjunit5 user guide了解详情 .

  • 1200

    我在这里尝试了很多方法,但它们要么很复杂,要么不能满足我的要求 . 事实上,人们可以非常简单地编写一个辅助方法:

    public class ExceptionAssertions {
        public static void assertException(BlastContainer blastContainer ) {
            boolean caughtException = false;
            try {
                blastContainer.test();
            } catch( Exception e ) {
                caughtException = true;
            }
            if( !caughtException ) {
                throw new AssertionFailedError("exception expected to be thrown, but was not");
            }
        }
        public static interface BlastContainer {
            public void test() throws Exception;
        }
    }
    

    像这样用它:

    assertException(new BlastContainer() {
        @Override
        public void test() throws Exception {
            doSomethingThatShouldExceptHere();
        }
    });
    

    零依赖:不需要mockito,不需要powermock;和最后的课程一起工作得很好 .

  • 35

    Java 8解决方案

    如果您想要一个解决方案:

    • 使用Java 8 lambda

    • 不依赖于任何JUnit魔法

    • 允许您在单个测试方法中检查多个异常

    • 检查测试方法中特定行集引发的异常,而不是整个测试方法中的任何未知行

    • 产生抛出的实际异常对象,以便您可以进一步检查它

    这是我写的一个实用函数:

    public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
    {
        try
        {
            runnable.run();
        }
        catch( Throwable throwable )
        {
            if( throwable instanceof AssertionError && throwable.getCause() != null )
                throwable = throwable.getCause(); //allows "assert x != null : new IllegalArgumentException();"
            assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
            assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
            @SuppressWarnings( "unchecked" )
            T result = (T)throwable;
            return result;
        }
        assert false; //expected exception was not thrown.
        return null; //to keep the compiler happy.
    }
    

    taken from my blog

    使用方法如下:

    @Test
    public void testThrows()
    {
        RuntimeException e = expectException( RuntimeException.class, () -> 
            {
                throw new RuntimeException( "fail!" );
            } );
        assert e.getMessage().equals( "fail!" );
    }
    
  • 29

    恕我直言,在JUnit中检查异常的最佳方法是try / catch / fail / assert模式:

    // 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()); 
    }
    

    assertTrue 可能对某些人来说有点强,所以 assertThat(e.getMessage(), containsString("the message"); 可能更好 .

相关问题