关于Java:您如何断言在JUnit 4测试中抛出某个异常?

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

如何使用JUnit4来测试某些代码引发异常?

我当然可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;

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

  assertTrue(thrown);
}

我记得有一个注解或者assert.xyz或者其他的东西,在这种情况下,它远没有那么笨拙,更符合junit的精神。


Junit 4对此提供了支持:

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

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


既然JUnit5已经发布了,最好的选择是使用Assertions.assertThrows()(见我的另一个答案)。

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

1
2
3
4
5
6
7
8
9
10
11
12
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测试将失败。

有关详细信息,请参阅本文


小心使用预期的异常,因为它只声明方法抛出了该异常,而不是测试中的特定代码行。

我倾向于将其用于测试参数验证,因为此类方法通常非常简单,但更复杂的测试可能更好地用于:

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

运用判断。


正如前面所回答的,JUnit中有许多处理异常的方法。但是在Java 8中还有另一种:使用lambda表达式。使用lambda表达式,我们可以实现如下语法:

1
2
3
4
5
6
7
8
9
10
@Test
public void verifiesTypeAndMessage() {
    assertThrown(new DummyService()::someMethod)
            .isInstanceOf(RuntimeException.class)
            .hasMessage("Runtime exception occurred")
            .hasMessageStartingWith("Runtime")
            .hasMessageEndingWith("occurred")
            .hasMessageContaining("exception")
            .hasNoCause();
}

断言引发接受一个函数接口,该接口的实例可以用lambda表达式、方法引用或构造函数引用创建。接受该接口将期望并准备处理异常时引发的断言。

这是一种相对简单但功能强大的技术。

看看这个描述这种技术的博客文章: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

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


在JUnit中,测试异常有四种方法。

  • 对于JUnit4.x,请使用测试注释的可选"expected"属性

    1
    2
    3
    4
    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
  • 对于JUnit4.x,使用ExpectedException规则

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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();
        }
    }
  • 您还可以使用JUnit3框架下广泛使用的经典Try/Catch方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @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,您还可以使用assertthrow,如下所示

    1
    2
    3
    4
    5
    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
        assertEquals("expected messages", exception.getMessage());
    }
  • 所以

    • 当只想测试异常类型时,使用第1种方法
    • 当您希望进一步测试异常消息时,使用其他三种方法
    • 如果您使用JUnit 3,则首选第3个
    • 如果你喜欢Junit 5,那么你应该喜欢第四个
  • 有关详细信息,您可以阅读此文档和JUnit5用户指南以了解详细信息。


TL DR;>

  • 方法:将预jdk8 recommend catchtry-古老的好块。(别忘了fail()to add a catchassertion before the block)>

  • 翻译后jdk8:assertj custom to ASSERT或lambda外行为。>

regardless of JUnit 4或5的JUnit。>

"龙的故事>

EN is possible to write自己做自己catchblock or the try-使用JUnit(@Test(expected = ...)or the @Rule ExpectedExceptionJUnit工具规则的特征)。>

但我知道这些方式不是优雅的和不明智的组合与其他readability井工具。moreover have some pitfalls Junit给模具。>

  • the try- You have to catch块写块写在行为测试和茶,茶assertion在catch块,but many that may be find this风格taht结束阅读测试interrupts of the流。你也Assert.failat the need to write an otherwise the end of the try梅小姐一块测试端of the assertions;PMD,FindBugs这样的问题将现货或声纳。>

  • @Test(expected = ...)is the feature as You can write less兴趣写作测试代码,然后supposedly to this is less易编码错误。但在一些地区拓荒是缺失的方法。>

    • if the needs to check Additional)试验例外原因or on the like the message(the messages是真的好有重要例外,例外可能not be型精度不够)。
    • 也期望在placed is as the in the method,depending is written on how the then the wrong测试代码测试代码可以把the part of the例外,该试验的假阳性和m not sure that PMD,FindBugs这样的提示或声纳会给在线队列。>

      1
      2
      3
      4
      5
      6
      7
      8
      @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
      }
  • ExpectedExceptionis also the规则attempt to fix the previous caveats安什么感觉到尴尬,but as to使用EN位uses安easymock知道期望用户风格,这风格很好。?可能有些方便,但如果你的后续行为驱动开发(BDD)或断言(AAA)整理法原则规则ExpectedException茶不适合那些写作风格。它可以从预留of that the same as the吃苦的问题@Testas the Way You depending where the Place,期望。>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @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
    }

    即使预期的例外placed before the is the statement EN测试,测试你的阅读流中断if the BDD的或后续的AAA。>

    see this also Comment of the author of在线发ExpectedExceptionJUnit。这是deprecates JUnit 4.13-beta-2机制:>

    Pull request #1519: Deprecate ExpectedException

    Ok.

    The method Assert.assertThrows provides a nicer way for verifying exceptions. In addition the use of ExpectedException is error-prone when used with other rules like TestWatcher because the order of rules is important in that case.

    Ok.

  • 我知道他们有这些上述期权的caveats load of not to式编码器,和clearly免疫错误。>

  • 有在项目的感知,这答案就在北promising creating,抓住它的例外。>

    as the description of the项目说,它让在在线式编码器队列写在fluent of the捕集和数据库提供一assertion for this的例外。你可以使用任何assertion hamcrest assertj类或库。>

    在快速的主页:taken from the example>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 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();

    As You can see the code is straightforward捕捉你的在线数据库,在网上thenis the特异性API,API使用别名,将安assertj(similar to using assertThat(ex).hasNoCause()...)。在一些点在项目relied在线- ancestor of the assertj ASSERT试验。编辑:is the项目似乎在酝酿8支持Java的lambda。>

    缺点:This has currently二库>

    • of this at the time is to say this noteworthy写作:图书馆是基于mockito恩创源在模拟1.x as the of the测试对象在场景。我mockito is not as this with updated概述图书馆工作中或最终的最终方法。甚至如果它是基于布尔mockito version 2 the current,require to this会宣布在全球模型(inline-mock-maker)者,不可能是你想要的东西,mockmaker as this has different that the mockmaker drawbacks正则。>

    • 它还需要另一个测试依赖项。好的。

    一旦库支持lambda,这些问题就不适用了,但是断言工具集将复制该功能。好的。

    综合考虑,如果您不想使用catch exception工具,我将推荐使用try--catch块的老方法,至少在jdk7之前。对于JDK8用户,您可能更喜欢使用断言j,因为它提供的断言可能不仅仅是断言异常。好的。

  • 有了JDK8,lambda就进入了测试场景,事实证明它们是断言异常行为的一种有趣方式。已经更新了assertj以提供一个良好的、流畅的API来断言异常行为。好的。

    以及带有断言j的样本测试:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    @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");
    }
  • 随着JUnit5的几乎完全重写,断言已经得到了一些改进,它们可能被证明是一种现成的正确断言异常的方法。但实际上断言API还是有点差,除了assertThrows之外没有其他东西。好的。

    1
    2
    3
    4
    5
    6
    7
    @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)断言J的易用性、可发现的API、快速的开发速度以及事实上的测试依赖性是JDK8的最佳解决方案,不管测试框架(JUnit与否),以前的JDK应该依赖于trycatch块,即使它们感觉笨重。好的。

    这个答案是从另一个不具有相同可见性的问题中复制的,我是同一个作者。好的。好啊。


    既然Junit 5发布了,最好的选择是使用Assertions.assertThrows()(参见Junit 5用户指南)。

    下面是一个示例,用于验证是否引发了异常,并使用真理对异常消息进行断言:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class FooTest {
      @Test
      public void doStuffThrowsIndexOutOfBoundsException() {
        Foo foo = new Foo();

        IndexOutOfBoundsException e = assertThrows(
            IndexOutOfBoundsException.class, foo::doStuff);

        assertThat(e).hasMessageThat().contains("woops!");
      }
    }

    与其他答案中的方法相比,优势在于:

  • 建在朱尼特
  • 如果lambda中的代码不引发异常,则会收到一条有用的异常消息;如果stacktrace引发不同的异常,则会收到一条stacktrace消息。
  • 简洁的
  • 允许测试遵循排列动作断言
  • 您可以精确地指示您希望抛出异常的代码
  • 你不需要在throws条款中列出预期的例外情况。
  • 您可以使用所选的断言框架对捕获的异常进行断言。
  • 类似的方法将在6月4.13日添加到org.junit Assert中。


    这个怎么样:捕获一个非常普通的异常,确保它不在catch块中,然后断言该异常的类是您期望的。如果a)异常类型错误(例如,如果改为使用空指针),以及b)从未引发异常,则此断言将失败。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public void testFooThrowsIndexOutOfBoundsException() {
      Throwable e = null;

      try {
        foo.doStuff();
      } catch (Throwable ex) {
        e = ex;
      }

      assertTrue(e instanceof IndexOutOfBoundsException);
    }


    BDD风格的解决方案:JUnit 4+catch exception+assertj

    1
    2
    3
    4
    5
    6
    7
    8
    @Test
    public void testFooThrowsIndexOutOfBoundsException() {

        when(foo).doStuff();

        then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);

    }

    源代码

    • https://gist.github.com/mariuszs/23f4e1857c28449b61

    依赖关系

    1
    eu.codearte.catch-exception:catch-exception:1.3.3


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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)好,因为它保证了测试中的预期行抛出了异常,并允许您检查有关异常的更多详细信息,例如消息,更容易:

    1
    2
    3
    4
    5
    6
    assertThatThrownBy(() ->
           {
             throw new Exception("boom!");
           })
        .isInstanceOf(Exception.class)
        .hasMessageContaining("boom");

    Maven/Gradle说明。


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

    用这个小助手你会写

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

    这比JUnit 4.7的ExpectedException规则更详细。与skaffman提供的解决方案相比,您可以指定期望异常出现在哪一行代码中。我希望这有帮助。


    更新:JUnit5改进了异常测试:assertThrows

    以下示例来自:JUnit 5用户指南

    1
    2
    3
    4
    5
    6
    7
    8
     @Test
    void exceptionTesting() {
        Throwable exception = assertThrows(IllegalArgumentException.class, () ->
        {
            throw new IllegalArgumentException("a message");
        });
        assertEquals("a message", exception.getMessage());
    }

    使用JUnit 4的原始答案。

    有几种方法可以测试是否引发了异常。在我的文章中,我还讨论了以下选项:如何用JUnit编写优秀的单元测试

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

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

    使用trycatch

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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规则进行测试。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @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中异常测试的内容,了解异常测试和bad.robot——期望异常JUnit规则。


    您也可以这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            assert false;
        } catch (IndexOutOfBoundsException e) {
            assert true;
        }
    }


    imho,在junit中检查异常的最佳方法是try/catch/fail/assert模式:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 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");可能更好。


    Junit 5解决方案

    1
    2
    3
    4
    5
    6
    @Test
    void testFooThrowsIndexOutOfBoundsException() {    
      Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );

      assertEquals("some message", exception.getMessage() );
    }

    关于JUnit5的更多信息,请访问http://junit.org/junit5/docs/current/user-guide/编写测试断言


    我在这里尝试了许多方法,但它们要么很复杂,要么不完全符合我的要求。实际上,可以非常简单地编写一个助手方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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;
        }
    }

    这样使用:

    1
    2
    3
    4
    5
    6
    assertException(new BlastContainer() {
        @Override
        public void test() throws Exception {
            doSomethingThatShouldExceptHere();
        }
    });

    零依赖性:不需要mockito,不需要powermock;并且可以与最终类一起工作。


    我在mkyong博客中找到的Junit4最灵活、最优雅的答案。它具有使用@Rule注释的try/catch的灵活性。我喜欢这种方法,因为您可以读取定制异常的特定属性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    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("");

        }

    }

    JUnit对此有内置支持,具有"expected"属性


    Java 8解决方案

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

    • 利用Java 8 LAMBDAS
    • 不依赖任何魔法
    • 允许您检查单个测试方法中的多个异常
    • 检查测试方法中的特定行集是否引发异常,而不是整个测试方法中的任何未知行。
    • 生成引发的实际异常对象,以便进一步检查它

    我写了一个实用函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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.
    }

    (摘自我的博客)

    使用方法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Test
    public void testThrows()
    {
        RuntimeException e = expectException( RuntimeException.class, () ->
            {
                throw new RuntimeException("fail!" );
            } );
        assert e.getMessage().equals("fail!" );
    }


    在我的例子中,我总是从数据库中得到runtimeexception,但是消息不同。异常需要分别处理。以下是我测试它的方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @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));
    }


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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();,然后:

    1
    2
    3
    4
    ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
    exception.expect(exMatch);
    someObject.somethingThatThrowsMyException();
    exMatch.off();

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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());
    }


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

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

    这提供了许多可以用来改进JUnit测试的特性。如果您看到下面的示例,我将对异常进行3项测试。

  • 引发的异常类型
  • 异常消息
  • 异常的原因
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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");
        }

    }

    除了Namshubvriter所说的,确保:

    • ExpectedException实例是公共的(相关问题)
    • ExpectedException未在say,@before方法中实例化。这篇文章清楚地解释了朱尼特执行命令的所有错综复杂之处。

    不要这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    @Rule    
    public ExpectedException expectedException;

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

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


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

    在Java 8中,像这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //given

    //when
    Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));

    //then
    AnyException anyException = (AnyException) throwable;
    assertThat(anyException.getMessage()).isEqualTo("........");
    assertThat(exception.getCode()).isEqualTo(".......);

    带有java8的JUnit4解决方案将使用此功能:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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));
    }

    然后使用:

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

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


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public int divideByZeroDemo(int a,int b){

        return a/b;
    }

    public void exceptionWithMessage(String [] arr){

        throw new ArrayIndexOutOfBoundsException("Array is out of bound");
    }

    上面的代码是测试可能发生的一些未知异常,下面的代码是用自定义消息断言一些异常。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     @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
    <hr><P>我的解决方案使用Java 8 lambdas:</P>[cc lang="
    java"]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
            }
        }
    }

    您必须定义一个功能接口,因为Runnable没有声明所需的throws

    1
    2
    3
    4
    @FunctionalInterface
    public interface ThrowingRunnable {
        void run() throws Throwable;
    }

    该方法的使用方法如下:

    1
    2
    3
    4
    5
    6
    7
    8
    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);

    使用Java 8,可以创建一种方法,该方法采用代码检查和预期异常作为参数:

    1
    2
    3
    4
    5
    6
    7
    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);
        }
      }

    然后在测试中:

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

    效益:

    • 不依赖任何图书馆
    • 局部检查-更精确,如果需要,允许在一个测试中有多个这样的断言
    • 易于使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        @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;

                   }

        }

    下面是另一种检查方法是否引发正确的异常的方法。


    编写测试用例有两种方法

  • 用方法抛出的异常来注释测试。像这样的东西
  • 您可以使用try catch块在测试类中捕获异常,并对从测试类中的方法抛出的消息进行断言。

    1
    2
    3
    4
    5
    6
    try{
    }
    catch(exception to be thrown from method e)
    {
         assertEquals("message", e.getmessage());
    }
  • 我希望这能回答你的问题快乐学习…


    我想对这个问题的解决方案发表评论,这样就避免了需要任何与异常相关的JUnit代码。

    我使用了asserttrue(布尔值)和try/catch来查找要抛出的预期异常。下面是一个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public void testConstructor() {
        boolean expectedExceptionThrown;
        try {
            // Call constructor with bad arguments
            double a = 1;
            double b = 2;
            double c = a + b; // In my example, this is an invalid option for c
            new Triangle(a, b, c);
            expectedExceptionThrown = false; // because it successfully constructed the object
        }
        catch(IllegalArgumentException e) {
            expectedExceptionThrown = true; // because I'm in this catch block
        }
        catch(Exception e) {
            expectedExceptionThrown = false; // because it threw an exception but not the one expected
        }
        assertTrue(expectedExceptionThrown);
    }


    1
    2
    3
    4
    5
    6
    try {
        my method();
        fail("This method must thrwo" );
    } catch (Exception ex) {
        assertThat(ex.getMessage()).isEqual(myErrormsg);
    }