当前位置: 首页 > 面试题库 >

如何验证未引发异常

通博实
2023-03-14
问题内容

在我的使用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; dr

  • JDK8之后:使用 AssertJ 或自定义lambda来声明 异常 行为。

  • JDK8之前的版本:我将html" target="_blank">推荐旧的好try- catch块。( 不要忘记fail()在该catch块之前添加一个断言

无论是Junit 4还是JUnit 5。

长话

可以自己编写自己 做的代码 try - catch阻止或使用JUnit工具(@Test(expected = ...)@Rule ExpectedExceptionJUnit规则功能)。

但是这些方法并不那么优雅,并且在 可读性方面没有 与其他工具很好地融合在一起。而且,JUnit工具确实存在一些陷阱。

  1. try- catch块,你必须写周围的测试行为块,写在catch块的断言,这可能是罚款,但很多人觉得这种风格中断测试的阅读流程。另外,您需要Assert.fail在代码try块的末尾写一个。否则,测试可能会遗漏断言的某一方面; PMDfindbugsSonar 会发现此类问题。

  2. @Test(expected = ...)功能很有趣,因为您可以编写更少的代码,然后编写此测试的代码据说不那么容易出错。 但是 在某些领域缺少这种方法。

    • 如果测试需要检查有关异常的其他内容,例如原因或消息(好的异常消息非常重要,那么具有精确的异常类型可能还不够)。
    • 同样,由于方法中的期望值很高,这取决于测试代码的编写方式,然后测试代码的错误部分会引发异常,从而导致测试结果呈假阳性,并且我不确定 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. 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,它也会破坏您的阅读流程。

另外,请参见的作者关于JUnit的注释问题ExpectedException。JUnit
4.13-beta-2
甚至不赞成使用此机制:

拉取请求#1519:弃用ExpectedException

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

因此,以上所有这些选项都有很多警告,并且显然无法避免编码错误。

  1. 创建这个答案后,我意识到一个项目看起来很有希望,那就是 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();

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

当前,该库有两个缺点:

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

* 它需要另一个测试依赖项。

一旦库支持lambda,这些问题将不再适用。但是,该功能将由AssertJ工具集复制。

如果您不想使用catch-exception工具,请考虑所有因素,我将建议使用try- catch块的旧方法,至少可以使用JDK7。对于JDK
8用户,您可能更喜欢使用AssertJ,因为它提供的不仅仅是断言异常。

  1. 使用JDK8,lambda进入了测试环境,事实证明它们是断言异常行为的一种有趣方式。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");
    }
  1. 通过对JUnit 5的近乎完全的重写,断言已得到了一些改进,作为断言正确地声明异常的开箱即用的方式,它们可能证明很有趣。但是实际上断言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)不论测试框架(JUnit )是什么, AssertJ 的易用性,可发现的API,快速的开发速度以及
事实上的 测试依赖性是使用JDK8的最佳解决方案。还是不可以),即使以前的JDK 感到笨拙,也应该改用 try -catch块。



 类似资料:
  • 我正在创建一个Spring Boot项目来测试我的rest API(非常非常小的项目)。该项目包含三个文件。 1)app.java(主程序)2)AppConfig(配置文件)3)主控制器(简单Rest控制器) 我添加了Spring-boot-starter-parent(1.4.0.release)和spring-boot-starter-web作为依赖项。 但是在执行时,会得到如下所示的异常。

  • 当我尝试测试此方法时 使用以下测试   也是如此。 是否可以使用AssertJ测试静态方法实际上抛出未检查的异常?我应该在考试中改变什么?

  • 例子: 我如何在mockito中执行此操作,并在强制抛出异常1后验证是否得到了异常2?

  • 问题内容: 如果参数无效,如何使用JSR-303验证方法参数并引发异常? 例如像这样:? 现在,我检查方法主体中的每个方法参数,例如 而且我觉得这很丑。 PS作为参考实现,我使用Hibernate-validator 4.1.0.Final 问题答案: 如果可以升级到Hibernate Validator 4.2.0或更高版本,则可以使用其方法验证功能,该功能为方法参数和返回值的验证提供支持。 可

  • 在测试中,我使用的是mockobject: 我试图验证其方法的使用: 但它抛出以下异常: 组织。莫基托。例外情况。滥用。UnfinishedVerificationException:此处缺少验证(模拟)的方法调用: 这一行的例外点是: setMaxRows接受一个int。 当我注释掉这一行时,测试成功了。调试程序时,我可以看到正在设置的setMaxRows方法: BrandLabels是一个Li

  • 我正在尝试使用@Valid验证我的JPA实体,如下所示: 它工作了一段时间,但现在它停止工作,我不知道为什么。我试着在< code>persist方法中手动执行,它按预期工作: 可能会发生什么情况,或者我该如何调试?