加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

java – 如何验证是否抛出异常

发布时间:2020-12-14 05:58:38 所属栏目:Java 来源:网络整理
导读:在我使用Mockito的单元测试中,我想验证是否没有抛出NullPointerException. public void testNPENotThrown{ Calling calling= Mock(Calling.class); testClass.setInner(calling); testClass.setThrow(true); testClass.testMethod(); verify(calling,never()
在我使用Mockito的单元测试中,我想验证是否没有抛出NullPointerException.
public void testNPENotThrown{
    Calling calling= Mock(Calling.class);
    testClass.setInner(calling);
    testClass.setThrow(true);

    testClass.testMethod();

    verify(calling,never()).method();
}

我的测试设置了testClass,设置了Calling对象和属性,以便该方法将抛出NullPointerException.

我验证Calling.method()从不被调用.

public void testMethod(){
    if(throw) {
        throw new NullPointerException();
    }

    calling.method();
}

我想要一个失败的测试,因为它抛出一个NullPointerException,然后我想写一些代码来解决这个问题.

我注意到的是,测试总是通过,因为异常从来没有被抛出测试方法.

解决方法

TL;博士

> pre-JDK8:我会推荐旧的好的try-catch块.
> post-JDK8:使用AssertJ或custom lambdas来表示异常行为.

漫长的故事

可以自己写一个自己尝试catch块或使用JUnit工具(@Test(expected = …)或@Rule ExpectedException JUnit规则功能).

但是这些方式并不那么优雅,并不能很好地与其他工具混合良好的可读性.

> try-catch块你必须围绕被测试的行为写入块,并在断块中写入断言,可能很好,但是很多查找taht这种风格会中断测试的读取流程.此外,您需要在try块的末尾写一个Assert.fail,否则测试可能会错过该断言的一边; PMD,findbugs或Sonar将会发现这样的问题.
> @Test(expected = …)功能很有趣,因为您可以编写更少的代码,然后编写此测试据说不太容易出现编码错误.但这种做法缺乏一些方面.

>如果测试需要检查异常的其他事情,如原因或消息(良好的异常消息非常重要,具有精确的异常类型可能不够).
>另外,由于期望在方法中放置,取决于测试代码的编写方式,所以测试代码的错误部分可以抛出异常,导致错误的正测试,我不知道PMD,findbugs或Sonar将会提供这样的代码的提示.

@Test(expected = WantedException.class)
public void call2_should_throw_a_WantedException__not_call1() {
    // init tested
    tested.call1(); // may throw a WantedException

    // call to be actually tested
    tested.call2(); // the call that is supposed to raise an exception
}

> ExpectedException规则也是尝试修复以前的注意事项,但使用它感觉有点尴尬,因为它使用期望风格,EasyMock用户非常了解这种风格.一些可能是方便的,但是如果遵循行为驱动开发(BDD)或安排行为断言(AAA)原则,ExpectedException规则将不适合这些写作风格.除此之外,它可能像@Test的方式一样受到同样的问题的影响,取决于你所期待的地方.

@Rule ExpectedException thrown = ExpectedException.none()

@Test
public void call2_should_throw_a_WantedException__not_call1() {
    // expectations
    thrown.expect(WantedException.class);
    thrown.expectMessage("boom");

    // init tested
    tested.call1(); // may throw a WantedException

    // call to be actually tested
    tested.call2(); // the call that is supposed to raise an exception
}

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

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

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

>在创建这个看起来很有前途的答案后,我发现了一个项目,这是catch-exception.

正如该项目的描述所示,它使编码器写入流畅的代码行捕获异常并提供此异常以供稍后断言.您也可以使用任何断言库,如Hamcrest或AssertJ.

从主页获取的快速例子:

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

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

目前这个图书馆有两个缺点:

>在撰写本文时,值得注意的是,这个库基于Mockito 1.x,因为它在场景后面创建了被测对象的模拟.由于Mockito尚未更新,因此该库无法使用最终类或最终方法.即使它是基于当前版本的mockito 2,这将需要声明一个全球模拟制造商(内联制造商),这可能不是你想要的,因为这个模拟器具有不同的缺点,即常规模拟器.
>它需要另一个测试依赖.

一旦图书馆支持lambdas,这些问题就不会适用,但AssertJ工具集将会复制这些功能.

考虑到如果你不想使用catch-exception工具,我会推荐try-catch块的旧的好方法,至少到JDK7.对于JDK 8用户,您可能更喜欢使用AssertJ,因为它可能不仅仅是断言异常.
>使用JDK8,羊羔进入测试场景,它们被证明是一种有趣的方式来证明异常行为. AssertJ已经更新,以提供一个很好的流畅的API来表示异常行为.

和AssertJ的样品测试:

@Test
public void test_exception_approach_1() {
    ...
    assertThatExceptionOfType(IOException.class)
            .isThrownBy(() -> someBadIOOperation())
            .withMessage("boom!"); 
}

@Test
public void test_exception_approach_2() {
    ...
    assertThatThrownBy(() -> someBadIOOperation())
            .isInstanceOf(Exception.class)
            .hasMessageContaining("boom");
}

@Test
public void test_exception_approach_3() {
    ...
    // when
    Throwable thrown = catchThrowable(() -> someBadIOOperation());

    // then
    assertThat(thrown).isInstanceOf(Exception.class)
                      .hasMessageContaining("boom");
}

>随着JUnit 5的接近完全重写,断言已经是improved,他们可能会被证明是一个开箱即用的方式来证明有效的例外.但是真正的断言API仍然有点差,assertThrows以外没有什么.

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

    Assertions.assertEquals("...",t.getMessage());
}

当你注意到assertEquals仍然返回void,因此不允许链接断言如AssertJ.

此外,如果您记住与Matcher或Assert的名称冲突,请准备与Assertions相同的冲突.

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读