简体   繁体   English

您如何断言在 JUnit 测试中抛出了某个异常?

[英]How do you assert that a certain exception is thrown in JUnit tests?

How can I use JUnit idiomatically to test that some code throws an exception?我如何惯用地使用 JUnit 来测试某些代码是否抛出异常?

While I can certainly do something like this:虽然我当然可以做这样的事情:

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

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

  assertTrue(thrown);
}

I recall that there is an annotation or an Assert.xyz or something that is far less kludgy and far more in-the-spirit of JUnit for these sorts of situations.我记得对于这些情况,有一个注释或 Assert.xyz 或JUnit的不那么笨拙而且更加精神的东西。

It depends on the JUnit version and what assert libraries you use.这取决于 JUnit 版本和您使用的断言库。

The original answer for JUnit <= 4.12 was: JUnit <= 4.12的原始答案是:

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {

    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);

}

Though answer https://stackoverflow.com/a/31826781/2986984 has more options for JUnit <= 4.12.虽然答案https://stackoverflow.com/a/31826781/2986984有更多 JUnit <= 4.12 的选项。

Reference :参考 :

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+).编辑:现在 JUnit 5 和 JUnit 4.13 已经发布,最好的选择是使用Assertions.assertThrows() (对于 JUnit 5)和Assert.assertThrows() (对于 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:如果您尚未迁移到 JUnit 5,但可以使用 JUnit 4.7,则可以使用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()这比@Test(expected=IndexOutOfBoundsException.class)好得多,因为如果在foo.doStuff()之前抛出IndexOutOfBoundsException ,测试将失败

See this article for details.有关详细信息,请参阅本文

Be careful using expected exception, because it only asserts that the method threw that exception, not a particular line of code in the test.小心使用预期异常,因为它只断言该方法抛出了该异常,而不是测试中的特定代码行

I tend to use this for testing parameter validation, because such methods are usually very simple, but more complex tests might better be served with:我倾向于使用它来测试参数验证,因为这些方法通常非常简单,但更复杂的测试可能会更好地使用:

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

Apply judgement.应用判断。

As answered before, there are many ways of dealing with exceptions in JUnit.如前所述,在 JUnit 中有多种处理异常的方法。 But with Java 8 there is another one: using Lambda Expressions.但是在 Java 8 中还有另一个:使用 Lambda 表达式。 With Lambda Expressions we can achieve a syntax like this:使用 Lambda 表达式,我们可以实现如下语法:

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

assertThrown accepts a functional interface, whose instances can be created with lambda expressions, method references, or constructor references. assertThrown 接受一个函数式接口,其实例可以使用 lambda 表达式、方法引用或构造函数引用创建。 assertThrown accepting that interface will expect and be ready to handle an exception.接受该接口的 assertThrown 将期望并准备好处理异常。

This is relatively simple yet powerful technique.这是一种相对简单但功能强大的技术。

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

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

Disclosure: I am the author of the blog and the project.披露:我是博客和项目的作者。

in junit, there are four ways to test exception.在junit中,有四种方法可以测试异常。

junit5.x junit5.x

  • for junit5.x, you can use assertThrows as following对于 junit5.x,您可以使用assertThrows如下

    @Test public void testFooThrowsIndexOutOfBoundsException() { Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff()); assertEquals("expected messages", exception.getMessage()); }

junit4.x junit4.x

  • for junit4.x, use the optional 'expected' attribute of Test annonation对于 junit4.x,使用 Test annonation 的可选 'expected' 属性

    @Test(expected = IndexOutOfBoundsException.class) public void testFooThrowsIndexOutOfBoundsException() { foo.doStuff(); }
  • for junit4.x, use the ExpectedException rule对于 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(); } }
  • you also can use the classic try/catch way widely used under junit 3 framework您还可以使用在 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. } }
  • so所以

    • if you like junit 5, then you should like the 1st one如果你喜欢junit 5,那么你应该喜欢第一个
    • the 2nd way is used when you only want test the type of exception当您只想测试异常类型时使用第二种方法
    • the first and last two are used when you want test exception message further当您想要进一步测试异常消息时使用第一个和最后两个
    • if you use junit 3, then the 4th one is preferred如果您使用 junit 3,则首选第 4 个
  • for more info, you can read this document and junit5 user guide for details.有关详细信息,您可以阅读此文档junit5 用户指南了解详细信息。

tl;dr tl;博士

  • post-JDK8 : Use AssertJ or custom lambdas to assert exceptional behaviour. post-JDK8 :使用AssertJ或自定义 lambda 断言异常行为。

  • pre-JDK8 : I will recommend the old good try - catch block. pre-JDK8:我会推荐旧的好try - catch块。 ( Don't forget to add a fail() assertion before the catch block ) 不要忘记在catch块之前添加一个fail()断言

Regardless of Junit 4 or JUnit 5.不管是 Junit 4 还是 JUnit 5。

the long story长篇大论

It is possible to write yourself a do it yourself try - catch block or use the JUnit tools ( @Test(expected = ...) or the @Rule ExpectedException JUnit rule feature).可以自己编写一个自己动手try - catch块或使用 JUnit 工具( @Test(expected = ...)@Rule ExpectedException JUnit 规则功能)。

But these ways are not so elegant and don't mix well readability wise with other tools.但是这些方法并不那么优雅,并且不能很好地将可读性与其他工具混合。 Moreover, JUnit tooling does have some pitfalls.此外,JUnit 工具确实存在一些缺陷。

  1. The try - catch block you have to write the block around the tested behavior and write the assertion in the catch block, that may be fine but many find that this style interrupts the reading flow of a test. try - catch块您必须围绕测试的行为编写块并在 catch 块中写入断言,这可能很好,但许多人发现这种样式会中断测试的读取流程。 Also, you need to write an Assert.fail at the end of the try block.此外,您需要在try块的末尾编写Assert.fail Otherwise, the test may miss one side of the assertions;否则,测试可能会错过断言的一侧; PMD , findbugs or Sonar will spot such issues. PMDfindbugsSonar会发现此类问题。

  2. The @Test(expected = ...) feature is interesting as you can write less code and then writing this test is supposedly less prone to coding errors. @Test(expected = ...)功能很有趣,因为您可以编写更少的代码,然后编写此测试据说不太容易出现编码错误。 But this approach is lacking in some areas.这种方法在某些领域是缺乏的。

    • If the test needs to check additional things on the exception like the cause or the message (good exception messages are really important, having a precise exception type may not be enough).如果测试需要检查异常的其他内容,例如原因或消息(好的异常消息非常重要,拥有精确的异常类型可能还不够)。
    • Also as the expectation is placed around in the method, depending on how the tested code is written then the wrong part of the test code can throw the exception, leading to false-positive test and I'm not sure that PMD , findbugs or Sonar will give hints on such code.此外,由于期望被放在方法中,根据测试代码的编写方式,测试代码的错误部分可能会引发异常,导致误报测试,我不确定PMDfindbugsSonar将提供有关此类代码的提示。

       @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 }
  3. The ExpectedException rule is also an attempt to fix the previous caveats, but it feels a bit awkward to use as it uses an expectation style, EasyMock users know very well this style. ExpectedException规则也是尝试修复之前的警告,但是使用起来感觉有点尴尬,因为它使用了期望样式, EasyMock用户非常了解这种样式。 It might be convenient for some, but if you follow Behaviour Driven Development (BDD) or Arrange Act Assert (AAA) principles the ExpectedException rule won't fit in those writing style.这对某些人来说可能很方便,但是如果您遵循行为驱动开发(BDD) 或安排行为断言(AAA) 原则, ExpectedException规则将不适合这些写作风格。 Aside from that it may suffer from the same issue as the @Test way, depending on where you place the expectation.除此之外,它可能会遇到与@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 }

    Even the expected exception is placed before the test statement, it breaks your reading flow if the tests follow BDD or AAA.即使预期的异常放在测试语句之前,如果测试遵循 BDD 或 AAA,它也会破坏您的阅读流程。

    Also, see this comment issue on JUnit of the author of ExpectedException .另外,请参阅ExpectedException作者的 JUnit 上的此评论问题。JUnit 4.13-beta-2 even deprecates this mechanism:JUnit 4.13-beta-2甚至弃用了这种机制:

    Pull request #1519 : Deprecate ExpectedException拉取请求 #1519 :弃用 ExpectedException

    The method Assert.assertThrows provides a nicer way for verifying exceptions. Assert.assertThrows 方法提供了一种更好的方法来验证异常。 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.此外,ExpectedException 的使用在与 TestWatcher 等其他规则一起使用时容易出错,因为在这种情况下规则的顺序很重要。

So these above options have all their load of caveats, and clearly not immune to coder errors.因此,上述这些选项有很多警告,显然不能避免编码错误。

  1. There's a project I became aware of after creating this answer that looks promising, it's catch-exception .在创建这个看起来很有希望的答案后,我意识到了一个项目,它是catch-exception

    As the description of the project says, it let a coder write in a fluent line of code catching the exception and offer this exception for the latter assertion.正如该项目的描述所说,它让编码人员用流畅的代码行编写捕获异常并为后一个断言提供此异常。 And you can use any assertion library like Hamcrest or AssertJ .您可以使用任何断言库,例如HamcrestAssertJ

    A rapid example taken from the home page :取自主页的快速示例:

     // 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 really straightforward, you catch the exception on a specific line, the then API is an alias that will use AssertJ APIs (similar to using assertThat(ex).hasNoCause()... ).如您所见,代码非常简单,您在特定行捕获异常, then API 是使用 AssertJ API 的别名(类似于使用assertThat(ex).hasNoCause()... )。 At some point the project relied on FEST-Assert the ancestor of AssertJ .在某些时候,该项目依赖于 AssertJ 的祖先 FEST-Assert EDIT: It seems the project is brewing a Java 8 Lambdas support.编辑:似乎该项目正在酝酿对 Java 8 Lambdas 的支持。

    Currently, this library has two shortcomings :目前,这个库有两个缺点:

    • At the time of this writing, it is noteworthy to say this library is based on Mockito 1.x as it creates a mock of the tested object behind the scene.在撰写本文时,值得注意的是该库基于 Mockito 1.x,因为它在幕后创建了测试对象的模拟。 As Mockito is still not updated this library cannot work with final classes or final methods .由于 Mockito 仍未更新,因此此库无法与最终类或最终方法一起使用 And even if it was based on Mockito 2 in the current version, this would require to declare a global mock maker ( inline-mock-maker ), something that may not what you want, as this mock maker has different drawbacks that the regular mock maker.即使它基于当前版本中的 Mockito 2,这也需要声明一个全局模拟制造商( inline-mock-maker ),这可能不是您想要的,因为这个模拟制造商与常规模拟具有不同的缺点制作者。

    • It requires yet another test dependency.它需要另一个测试依赖项。

    These issues won't apply once the library supports lambdas.一旦库支持 lambda,这些问题将不适用。 However, the functionality will be duplicated by the AssertJ toolset.但是,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.综合考虑如果不想使用catch-exception工具,我会推荐try - catch块的老好办法,至少到JDK7。 And for JDK 8 users you might prefer to use AssertJ as it offers may more than just asserting exceptions.对于 JDK 8 用户,您可能更喜欢使用 AssertJ,因为它提供的可能不仅仅是断言异常。

  2. With the JDK8, lambdas enter the test scene, and they have proved to be an interesting way to assert exceptional behaviour.在 JDK8 中,lambda 进入了测试场景,事实证明它们是一种断言异常行为的有趣方式。 AssertJ has been updated to provide a nice fluent API to assert exceptional behaviour. AssertJ 已经更新,提供了一个很好的流畅的 API 来断言异常行为。

    And a sample test with AssertJ :以及使用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"); }
  3. With a near-complete rewrite of JUnit 5, assertions have been improved a bit, they may prove interesting as an out of the box way to assert properly exception.随着对 JUnit 5 的近乎完整的重写,断言得到了一些改进,它们可能被证明是一种有趣的开箱即用的方式来正确断言异常。 But really the assertion API is still a bit poor, there's nothing outside assertThrows .但实际上断言 API 仍然有点差,除了assertThrows之外什么都没有。

     @Test @DisplayName("throws EmptyStackException when peeked") void throwsExceptionWhenPeeked() { Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek()); Assertions.assertEquals("...", t.getMessage()); }

    As you noticed assertEquals is still returning void , and as such doesn't allow chaining assertions like AssertJ.正如您注意到的那样, assertEquals仍然返回void ,因此不允许像 AssertJ 这样的链接断言。

    Also if you remember name clash with Matcher or Assert , be prepared to meet the same clash with Assertions .此外,如果您记得与MatcherAssert的名称冲突,请准备好与Assertions遇到相同的冲突。

I'd like to conclude that today (2017-03-03) AssertJ 's ease of use, discoverable API, the rapid pace of development and as a de facto test dependency is the best solution with JDK8 regardless of the test framework (JUnit or not), prior JDKs should instead rely on try - catch blocks even if they feel clunky.我想得出结论,今天 (2017-03-03) AssertJ的易用性、可发现的 API、快速的开发速度以及作为事实上的测试依赖项是 JDK8 的最佳解决方案,无论测试框架如何(JUnit与否),以前的 JDK 应该改为依赖try - catch块,即使它们感觉很笨重。

This answer has been copied from another question that don't have the same visibility, I am the same author.这个答案是从另一个不具有相同可见性的问题中复制的,我是同一位作者。

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).现在 JUnit 5 和 JUnit 4.13 已经发布,最好的选择是使用Assertions.assertThrows() (对于 JUnit 5)和Assert.assertThrows() (对于 JUnit 4.13)。 See the JUnit 5 User Guide .请参阅JUnit 5 用户指南

Here is an example that verifies an exception is thrown, and uses Truth to make assertions on the exception message:这是一个验证抛出异常的示例,并使用Truth对异常消息进行断言:

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内置于 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如果 lambda 中的代码没有抛出异常,您会收到一条有用的异常消息,如果它抛出不同的异常,您会收到一个堆栈跟踪
  3. Concise简洁的
  4. Allows your tests to follow Arrange-Act-Assert允许您的测试遵循 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您不需要在throws子句中列出预期的异常
  7. You can use the assertion framework of your choice to make assertions about the caught exception您可以使用您选择的断言框架对捕获的异常进行断言

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.怎么样:捕获一个非常普遍的异常,确保它脱离了 catch 块,然后断言异常的类是你所期望的。 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.如果 a) 异常的类型错误(例如,如果您有一个空指针)并且 b) 从未抛出异常,则此断言将失败。

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

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

  assertTrue(e instanceof IndexOutOfBoundsException);
}

Update: JUnit5 has an improvement for exceptions testing: assertThrows .更新: JUnit5 对异常测试进行了改进: assertThrows

The following example is from: Junit 5 User Guide以下示例来自: Junit 5 用户指南

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

Original answer using JUnit 4.使用 JUnit 4 的原始答案。

There are several ways to test that an exception is thrown.有几种方法可以测试是否引发了异常。 I have also discussed the below options in my post How to write great unit tests with JUnit我还在我的帖子How to write great unit tests with JUnit中讨论了以下选项

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

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

Using try catch使用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!"));
    }
     
}

Testing with ExpectedException Rule.使用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");
}

You could read more about exceptions testing in JUnit4 wiki for Exception testing and bad.robot - Expecting Exceptions JUnit Rule .您可以在JUnit4 wiki 中阅读有关异常测试的更多信息,以获取 Exception testingbad.robot - Expecting Exceptions JUnit Rule

Using an AssertJ assertion, which can be used alongside JUnit:使用AssertJ断言,它可以与 JUnit 一起使用:

import static org.assertj.core.api.Assertions.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();

  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}

It's better than @Test(expected=IndexOutOfBoundsException.class) because it guarantees the expected line in the test threw the exception and lets you check more details about the exception, such as message, easier:它比@Test(expected=IndexOutOfBoundsException.class)更好,因为它保证了测试中的预期行引发了异常,并让您可以更轻松地检查有关异常的更多详细信息,例如消息:

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

Maven/Gradle instructions here. 此处的 Maven/Gradle 说明。

BDD Style Solution: JUnit 4 + Catch Exception + AssertJ BDD风格解决方案: JUnit 4 + Catch Exception + AssertJ

import static com.googlecode.catchexception.apis.BDDCatchException.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {

    when(() -> foo.doStuff());

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

}

Dependencies依赖项

eu.codearte.catch-exception:catch-exception:2.0

To solve the same problem I did set up a small project: http://code.google.com/p/catch-exception/为了解决同样的问题,我建立了一个小项目: http ://code.google.com/p/catch-exception/

Using this little helper you would write使用这个小助手你会写

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

This is less verbose than the ExpectedException rule of JUnit 4.7.这比 JUnit 4.7 的 ExpectedException 规则更简洁。 In comparison to the solution provided by skaffman, you can specify in which line of code you expect the exception.与 skaffman 提供的解决方案相比,您可以指定您期望在哪一行代码中出现异常。 I hope this helps.我希望这有帮助。

You can also do this:你也可以这样做:

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

IMHO, the best way to check for exceptions in JUnit is the try/catch/fail/assert pattern:恕我直言,在 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()); 
}

The assertTrue might be a bit strong for some people, so assertThat(e.getMessage(), containsString("the message"); might be preferable. assertTrue对某些人来说可能有点强,所以assertThat(e.getMessage(), containsString("the message");可能更可取。

JUnit 5 Solution JUnit 5 解决方案

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

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

The most flexible and elegant answer for Junit 4 I found in the Mkyong blog .我在Mkyong 博客中找到的 Junit 4 最灵活和优雅的答案。 It has the flexibility of the try/catch using the @Rule annotation.它具有使用@Rule注释的try/catch的灵活性。 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("");

    }

}

I tried many of the methods here, but they were either complicated or didn't quite meet my requirements.我在这里尝试了很多方法,但它们要么很复杂,要么不太符合我的要求。 In fact, one can write a helper method quite simply:事实上,可以很简单地编写一个辅助方法:

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

Use it like this:像这样使用它:

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

Zero dependencies: no need for mockito, no need powermock;零依赖:不需要mockito,不需要powermock; and works just fine with final classes.并且适用于最终课程。

JUnit 对此具有内置支持,具有“预期”属性

Java 8 solution Java 8 解决方案

If you would like a solution which:如果您想要一个解决方案:

  • Utilizes Java 8 lambdas利用 Java 8 lambda
  • Does not depend on any JUnit magic依赖于任何 JUnit 魔法
  • Allows you to check for multiple exceptions within a single test method允许您在单个测试方法中检查多个异常
  • Checks for an exception being thrown by a specific set of lines within your test method instead of any unknown line in the entire test method检查测试方法中的一组特定行引发的异常,而不是整个测试方法中的任何未知行
  • Yields the actual exception object that was thrown so that you can further examine it产生实际抛出的异常对象,以便您可以进一步检查它

Here is a utility function that I wrote:这是我写的一个实用函数:

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 testing for "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 ) 取自我的博客

Use it as follows:按如下方式使用它:

@Test
public void testMyFunction()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            myFunction();
        } );
    assert e.getMessage().equals( "I haz fail!" );
}

public void myFunction()
{
    throw new RuntimeException( "I haz fail!" );
}

In my case I always get RuntimeException from db, but messages differ.就我而言,我总是从 db 获得 RuntimeException,但消息不同。 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));
}

Just make a Matcher that can be turned off and on, like this:只需制作一个可以关闭和打开的 Matcher,如下所示:

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();添加public ExpectedException exception = ExpectedException.none(); , then: , 然后:

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

We can use an assertion fail after the method that must return an exception:我们可以在必须返回异常的方法之后使用断言失败:

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在 JUnit 4 或更高版本中,您可以按如下方式测试异常

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


this provides a lot of features which can be used to improve our JUnit tests.这提供了许多可用于改进 JUnit 测试的功能。
If you see the below example I am testing 3 things on the exception.如果您看到下面的示例,我正在对异常进行 3 项测试。

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

}

Additionally to what NamShubWriter has said, make sure that:除了NamShubWriter所说的,请确保:

  • The ExpectedException instance is public ( Related Question ) ExpectedException 实例是公开的( 相关问题
  • The ExpectedException isn't instantiated in say, the @Before method. ExpectedException没有在 @Before 方法中实例化。 This post clearly explains all the intricacies of JUnit's order of execution.这篇文章清楚地解释了 JUnit 执行顺序的所有复杂性。

Do not do this:要这样做:

@Rule    
public ExpectedException expectedException;

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

Finally, this blog post clearly illustrates how to assert that a certain exception is thrown.最后, 这篇博文清楚地说明了如何断言某个异常被抛出。

Junit4 solution with Java8 is to use this function:使用 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));
}

Usage is then:那么用法是:

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

Note that the only limitation is to use a final object reference in lambda expression.请注意,唯一的限制是在 lambda 表达式中使用final对象引用。 This solution allows to continue test assertions instead of expecting thowable at method level using @Test(expected = IndexOutOfBoundsException.class) solution.该解决方案允许使用@Test(expected = IndexOutOfBoundsException.class)解决方案继续测试断言,而不是期望在方法级别上进行测试。

I recomend library assertj-core to handle exception in junit test我推荐库assertj-core来处理 junit 测试中的异常

In java 8, like this:在 java 8 中,像这样:

//given

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

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

JUnit framework has assertThrows() method: JUnit 框架有assertThrows()方法:

ArithmeticException exception = assertThrows(ArithmeticException.class, () ->
    calculator.divide(1, 0));
assertEquals("/ by zero", exception.getMessage());

Take for example, you want to write Junit for below mentioned code fragment举个例子,你想为下面提到的代码片段编写 Junit

public int divideByZeroDemo(int a,int b){

    return a/b;
}

public void exceptionWithMessage(String [] arr){

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

The above code is to test for some unknown exception that may occur and the below one is to assert some exception with custom message.上面的代码是测试可能发生的一些未知异常,下面的代码是用自定义消息断言一些异常。

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

With Java 8 you can create a method taking a code to check and expected exception as parameters:使用 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);
    }
  }

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便于使用
    @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.这是另一种检查方法是否抛出正确异常的方法。

My solution using Java 8 lambdas:我使用 Java 8 lambda 的解决方案:

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

You have to define a FunctionalInterface, because Runnable doesn't declare the required throws .您必须定义一个 FunctionalInterface,因为Runnable没有声明所需的throws

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

The method can be used as follows:该方法可以使用如下:

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

There are two ways of writing test case编写测试用例有两种方式

  1. Annotate the test with the exception which is thrown by the method.用方法抛出的异常注释测试。 Something like this @Test(expected = IndexOutOfBoundsException.class)像这样的东西@Test(expected = IndexOutOfBoundsException.class)
  2. You can simply catch the exception in the test class using the try catch block and assert on the message that is thrown from the method in test class.您可以使用 try catch 块简单地捕获测试类中的异常,并对从测试类中的方法抛出的消息进行断言。

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

I hope this answers your query Happy learning...我希望这能回答您的问题 学习愉快...

I would use assertThatThrownBy我会使用assertThatThrownBy

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  assertThatThrownBy(() -> doStuff()).isInstanceOf(IndexOutOfBoundsException.class)
}```

I wanted to comment with my solution to this problem, which avoided needing any of the exception related JUnit code.我想评论我对这个问题的解决方案,它避免了需要任何与异常相关的 JUnit 代码。

I used assertTrue(boolean) combined with try/catch to look for my expected exception to be thrown.我将 assertTrue(boolean) 与 try/catch 结合使用来查找要抛出的预期异常。 Here's an example:这是一个例子:

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);
}
try {
    my method();
    fail( "This method must thrwo" );
} catch (Exception ex) {
    assertThat(ex.getMessage()).isEqual(myErrormsg);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM