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

SCd*_*CdF 1915 java junit assert exception junit4

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

虽然我当然可以这样做:

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

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

  assertTrue(thrown);
}
Run Code Online (Sandbox Code Playgroud)

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

ska*_*man 2291

JUnit 4支持这个:

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

  • Roy Osherove在单元测试*的艺术中不鼓励进行这种异常测试,因为Exception可能在测试中的任何地方,而不仅仅是在被测单元内部. (71认同)
  • 如果你只想在代码中的某个地方出现异常,而不是像这样的毯子,那么这段代码将不起作用. (65认同)
  • 我不同意@ Kiview/Roy Osherove.在我看来,测试应该是行为,而不是实现.通过测试特定方法可能引发错误,您将测试直接绑定到实现.我认为用上面显示的方法进行测试可以提供更有价值的测试.我要补充的警告是,在这种情况下,我会测试一个自定义异常,以便我知道我得到了我真正想要的异常. (21认同)
  • 都不是.我想测试类的行为.重要的是,如果我尝试检索不存在的东西,我会得到一个例外.事实上数据结构是`ArrayList`响应`get()`是无关紧要的.如果我将来选择移动到原始数组,那么我将不得不改变这个测试实现.应隐藏数据结构,以便测试可以关注*class*的行为. (5认同)
  • @skaffman这不适用于org.junit.experimental.theories.Theory运行org.junit.experimental.theories.Theories (4认同)
  • @nickbdyer 我认为这里存在一些误解。Kiview/Kevin Wittek 说测试中使用的任何方法都可能抛出异常并导致测试通过,而不会到达实际应该抛出该类型异常的语句。所使用的数据结构与他的观点无关。如果该结构包含在被测类的方法中,那么您是对的,该类中的哪个语句抛出异常并不重要,但他并不矛盾。 (3认同)
  • @nickbdyer当然要测试行为。但是你想测试 `ArrayList` 构造函数或 `get()` 方法的行为吗?在上面的示例中,您正在测试两者。如果您的构造函数抛出此异常,则测试将通过,尽管您想测试 `get()` 方法。 (2认同)

Nam*_*ter 1280

编辑现在JUnit5已经发布,最好的选择是使用Assertions.assertThrows()(参见我的其他答案).

如果您尚未迁移到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();
  }
}
Run Code Online (Sandbox Code Playgroud)

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

看到 详细信息,此文

  • @skaffman - 如果我已经正确理解了这一点,看起来异常只在一个测试中应用了exception.expect,而不是整个类. (14认同)
  • 这是最好的方法.与斯卡弗曼的解决方案相比,这里有两个优点.首先,`ExpectedException`类具有匹配异常消息的方法,甚至可以编写依赖于异常类的自己的匹配器.其次,您可以在您希望抛出异常的代码行之前设置您的期望 - 这意味着如果错误的代码行抛出异常,您的测试将失败; 而对于skaffman的解决方案,没有办法做到这一点. (9认同)
  • 如果我们期望抛出的异常是一个经过检查的异常,我们应该添加throws或try-catch还是以另一种方式测试这种情况? (5认同)
  • @MartinTrummer在foo.doStuff()之后不应该运行代码,因为抛出异常并退出该方法.在预期的异常之后有代码(除了在finally中关闭资源之外)无论如何都是无益的,因为如果抛出异常它永远不会被执行. (5认同)
  • @MJafarMash如果检查了您希望抛出的异常,那么您将该异常添加到测试方法的throws子句中.在测试声明抛出已检查异常的方法时,即使在特定测试用例中未触发异常,也会执行相同的操作. (5认同)

dav*_*veb 464

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

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

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

运用判断.

  • 也许我老了,但我还是喜欢这个.它还为我提供了一个测试异常本身的地方:有时我对某些值的getter有异常,或者我可能只是在消息中查找特定值(例如在消息"unrecognized code'xyz'中查找"xyz" "). (92认同)
  • @ user1154664实际上,你不能.使用ExpectedException,您只能测试一个方法是否抛出异常,因为当调用该方法时,测试将停止执行,因为它抛出了预期的异常! (10认同)
  • 使用ExpectedException,你可以调用N exception.expect per方法来测试这个exception.expect(IndexOutOfBoundsException.class); foo.doStuff1(); exception.expect(IndexOutOfBoundsException.class); foo.doStuff2(); exception.expect(IndexOutOfBoundsException.class); foo.doStuff3(); (4认同)
  • 我认为NamshubWriter的方法可以为您提供两全其美的体验. (3认同)
  • 你的第一句话是不正确的。使用“ExpectedException”时,通常要做的是在您期望抛出异常的行之前立即设置期望值。这样,如果前面的行抛出异常,则不会触发规则,并且测试将失败。 (2认同)

Raf*_*iec 210

如前所述,有许多方法可以处理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();
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • @CristianoFontes这个API的更简单版本定于JUnit 4.13.请参阅https://github.com/junit-team/junit/commit/bdb1799a6b58c4ab40c418111333f4e8e1816e7e (6认同)
  • 我喜欢这个解决方案,但我可以从maven回购下载吗? (2认同)

wal*_*lsh 139

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

  • 对于junit4.x,使用Test annonation的可选'expected'属性

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
        assertEquals("expected messages", exception.getMessage());
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 对于junit4.x,请使用ExpectedException规则

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 你也可以使用在junit 3框架下广泛使用的经典try/catch方式

    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();
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 最后,对于junit5.x,您还可以使用assertThrows,如下所示

    @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.
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 所以

    • 当您只想测试异常类型时,使用第一种方法
    • 当您想要进一步测试异常消息时,将使用其他三种方法
    • 如果你使用junit 3,那么第三个是首选
    • 如果你喜欢junit 5,那么你应该喜欢第4个
  • 有关详细信息,请参阅此文档junit5用户指南以获取详细信息.

  • 对我来说,这是最好的答案,它涵盖了所有的办法很清楚,谢谢!我个人继续使用第三个选项,即使Junit4的可读性,避免空的catch块,你还可以赶上的Throwable和断言电子商务类型 (6认同)
  • 我要指出的是,Junit 4.13(重点是“.13”)...支持断言抛出。我赞成,因为这是一个很好的“完整概述”答案,但我希望您能考虑 4.13 的警告。 (2认同)

Bri*_*ice 106

TL;博士

  • 预JDK8:我会建议老好try- catch块.(不要忘记fail()catch块之前添加断言)

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

无论是Junit 4还是JUnit 5.

长话故事

这是可能自己写一个自己做的 try - catch块或使用JUnit工具(@Test(expected = ...)@Rule ExpectedExceptionJUnit的规则功能).

但是这些方式并不那么优雅,并且不能将可读性与其他工具完美结合在一起.

  1. try- catch块,你必须写周围的测试行为块,并写在catch块的断言,这可能是罚款,但很多人发现taht这种风格中断测试的阅读流程.你还需要Assert.failtry块的末尾写一个,否则测试可能会错过断言的一面; PMD,findbugsSonar将发现此类问题.

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

    • 如果测试需要检查异常上的其他内容,例如原因或消息(良好的异常消息非常重要,那么具有精确的异常类型可能还不够).
    • 此外,由于期望放在方法中,取决于测试代码的编写方式,然后测试代码的错误部分可能抛出异常,导致误报测试,我不确定PMD,findbugsSonar会给出这些代码的提示.

      @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
      }
      
      Run Code Online (Sandbox Code Playgroud)
  3. 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
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

    另请参阅作者JUnit上的此评论问题ExpectedException.JUnit 4.13-beta-2甚至不赞成这种机制:

    拉请求#1519:不推荐使用ExpectedException

    方法Assert.assertThrows为验证异常提供了一种更好的方法.此外,与TestWatcher等其他规则一起使用时,ExpectedException的使用容易出错,因为在这种情况下规则的顺序很重要.

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

  1. 在创建了这个看起来很有前途的答案后,我发现了一个项目,这是一个例外.

    正如项目描述所说,它让编码器用流畅的代码行编写代码来捕获异常并为以后的断言提供此异常.您可以使用任何断言库,如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();
    
    Run Code Online (Sandbox Code Playgroud)

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

    目前这个库有两个缺点:

    • 在撰写本文时,值得注意的是,该库基于Mockito 1.x,因为它创建了场景背后测试对象的模拟.由于Mockito仍未更新,因此该库无法使用最终类或最终方法.即使它是基于当前版本中的mockito 2,这也需要声明一个全局模拟制造商(inline-mock-maker),这可能不是你想要的东西,因为这个模拟器具有与常规模拟器不同的缺点.

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

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

    如果您不想使用catch-exception工具,请考虑所有因素,我将推荐try- catchblock 的旧方法,至少是JDK7.对于JDK 8用户,您可能更喜欢使用AssertJ,因为它提供的可能不仅仅是声明异常.

  2. 使用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");
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 通过几乎完全重写JUnit 5,断言得到了一点改进,它们可能被证明是一种开箱即用的方式来断言正确的异常.但实际上断言API仍然有点差,外面什么都没有assertThrows.

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

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

    此外,如果你记得名字冲突MatcherAssert,准备遇到同样的冲突Assertions.

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

这个答案是从另一个没有相同可见性的问题复制而来的,我是同一个作者.


Nam*_*ter 56

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

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

public class FooTest {
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

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

    assertThat(e).hasMessageThat().contains("woops!");
  }
}
Run Code Online (Sandbox Code Playgroud)

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

  1. 内置于JUnit中
  2. 如果lambda中的代码没有抛出异常,则会得到一个有用的异常消息,如果它抛出另一个异常,则会得到一个栈跟踪
  3. 简洁
  4. 允许您的测试遵循Arrange-Act-Assert
  5. 您可以准确地指出您希望抛​​出异常的代码
  6. 您不需要在throws子句中列出预期的异常
  7. 您可以使用您选择的断言框架来对捕获的异常进行断言

org.junit AssertJUnit 4.13 中将添加类似的方法.


Joh*_*han 40

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

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

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

  assertTrue(e instanceof IndexOutOfBoundsException);
}
Run Code Online (Sandbox Code Playgroud)

  • 这就是你在JUnit 3中所做的.Junit 4做得更好. (4认同)
  • 此外,当测试失败的那一天,您将看不到测试结果中的Exception ex是什么类型. (3认同)

Mar*_*szS 36

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

}
Run Code Online (Sandbox Code Playgroud)

源代码

依赖

eu.codearte.catch-exception:catch-exception:2.0
Run Code Online (Sandbox Code Playgroud)


wes*_*ton 36

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

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

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

  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}
Run Code Online (Sandbox Code Playgroud)

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

assertThatThrownBy(() ->
       {
         throw new Exception("boom!");
       })
    .isInstanceOf(Exception.class)
    .hasMessageContaining("boom");
Run Code Online (Sandbox Code Playgroud)

Maven/Gradle说明在这里.


rwi*_*zel 32

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

使用这个小助手你会写

verifyException(foo, IndexOutOfBoundsException.class).doStuff();
Run Code Online (Sandbox Code Playgroud)

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


Dil*_*sha 30

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

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

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

使用JUnit 4的原始答案.

有几种方法可以测试抛出异常.我还在我的帖子中讨论了以下选项如何使用JUnit编写出色的单元测试

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

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

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

}
Run Code Online (Sandbox Code Playgroud)

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");
}
Run Code Online (Sandbox Code Playgroud)

您可以在JUnit4 wiki中阅读有关异常测试bad.robot的异常测试的更多信息 - 期待异常JUnit规则.


Joh*_*kic 21

你也可以这样做:

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

  • 在JUnit测试中,最好使用`Assert.fail()`,而不是`assert`,以防你的测试在没有启用断言的环境中运行. (12认同)

Ale*_*ins 13

恕我直言,在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()); 
}
Run Code Online (Sandbox Code Playgroud)

assertTrue可能会有点强对某些人来说,这样assertThat(e.getMessage(), containsString("the message");可能是可取的.


Dan*_*fer 12

JUnit 5解决方案

@Test
void testFooThrowsIndexOutOfBoundsException() {    
  Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );

  assertEquals( "some message", exception.getMessage() );
}
Run Code Online (Sandbox Code Playgroud)

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


Hug*_*ins 11

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

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

assertException(new BlastContainer() {
    @Override
    public void test() throws Exception {
        doSomethingThatShouldExceptHere();
    }
});
Run Code Online (Sandbox Code Playgroud)

零依赖:不需要mockito,不需要powermock; 并且在最后的课程中工作得很好.


Dhe*_*rik 11

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

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

    }

}
Run Code Online (Sandbox Code Playgroud)


Mik*_*kis 9

Java 8解决方案

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

  • 利用Java 8 lambdas
  • 难道依赖于任何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.
}
Run Code Online (Sandbox Code Playgroud)

(摘自我的博客)

使用方法如下:

@Test
public void testThrows()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            throw new RuntimeException( "fail!" );
        } );
    assert e.getMessage().equals( "fail!" );
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*sey 8

JUnit具有内置的支持,具有"预期"属性


Mac*_*tow 7

在我的情况下,我总是从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));
}
Run Code Online (Sandbox Code Playgroud)


Tor*_*r P 5

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

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用它:

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

ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();
Run Code Online (Sandbox Code Playgroud)


She*_*uky 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());
}
Run Code Online (Sandbox Code Playgroud)

  • 如果抛出其他异常,第二个`catch`将吞下堆栈跟踪,丢失有用的信息 (3认同)

Bru*_*yne 5

除了NamShubWriter所说的之外,请确保:

  • ExpectedException 实例是公共的相关问题
  • ExpectedException在 @Before 方法中实例化。这篇文章清楚地解释了 JUnit 执行顺序的所有复杂性。

难道不是这样做:

@Rule    
public ExpectedException expectedException;

@Before
public void setup()
{
    expectedException = ExpectedException.none();
}
Run Code Online (Sandbox Code Playgroud)

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


Job*_*eph 5

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

@Rule
public ExpectedException exceptions = ExpectedException.none();
Run Code Online (Sandbox Code Playgroud)


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

  1. 抛出的异常类型
  2. 消息例外
  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");
    }

}
Run Code Online (Sandbox Code Playgroud)