单元测试的捕获与抛出异常

时间:2020-01-11 20:57:38

标签: java junit

在下面的代码中,ClassToTest.throwsException()引发了一个已检查的异常。我的偏好是将抛出异常添加到JUnit方法签名中,而不是将尝试抛出异常的代码包装在try catch中。这种偏好仅基于我认为更整洁的事实。我在这里违反编码规范吗?从测试的角度来看,在测试方法中引发异常与try / catch的功能是否有所不同?

import org.junit.Test;

public class TestingClass {


    @Test
    public void getDataTest() throws Exception {

        new ClassToTest().throwsException();

    }

}

与:

import org.junit.Test;

public class TestingClass {


    @Test
    public void getDataTest() {

        try {
            new ClassToTest().throwsException();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

ClassToTest.java:

public class ClassToTest {

    public void throwsException() throws Exception {

    }
}

2 个答案:

答案 0 :(得分:2)

只需将throws子句添加到方法声明中,并使异常冒泡,如JUnit4 FAQ - How do I write a test that fails when an unexpected exception is thrown?中所述:

在测试方法的throws子句中声明异常,并且不要在测试方法中捕获该异常。未捕获的异常将导致测试失败并显示错误。

下面是一个示例测试,当引发IndexOutOfBoundsException时失败:

@Test
public void testIndexOutOfBoundsExceptionNotRaised()
    throws IndexOutOfBoundsException {

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

IndexOutOfBoundsException在这里可能不是一个很好的例子,因为它是RuntimeException,但是对于其他例外情况,方法是相同的。

答案 1 :(得分:2)

如果不希望抛出Exception,则在代码中添加try-catch块没有任何额外的好处。更糟糕的是,仅添加try-catch块会导致测试通过。要使测试失败,必须向fail()添加一个调用才能使测试实际失败。这可能是错误的来源(如果有人忘记打电话给fail())。


出于完整性考虑,我将简短地讨论如何验证是否抛出了某个Exception。我想到了三种方法。

第一次尝试时,可以使用try-catch,但是在应该抛出预期的fail()的调用之后的try块中添加了Exception 。然后,在catch块中,将捕获预期的Exception。所有其他Exception将被重新抛出,因此测试将失败。它具有与上述同级产品相同的缺点。

第二,通过使用@Test(expected = ExpectedException.class)注释测试本身,存在JUnit4方式。乍一看似乎很整洁,但是却破坏了Given-When-Then structure测试,常常导致如下所示的测试:

@Test(expected = ArrayIndexOutOfBoundsException.class)
public void test() {
    // GIVEN
    final int[] array = new int[10];

    // WHEN
    final int value = array[10];

   // THEN: an ArrayIndexOutOfBoundsException should be thrown
}

没关系。

最后,通过将实际调用包装到assertThrows(...)的调用中,可以使用JUnit5:

@Test(expected = ArrayIndexOutOfBoundsException.class)
void test() {
    // GIVEN
    final int[] array = new int[10];

    // WHEN
    final Exception e = assertThrows(ArrayIndexOutOfBoundsException.class,
            () -> {
                int value = array[10];
            }
    );

   // THEN
   assertTrue(e.getMessage().contains("10"));
}

尽管这仍然不能正确地将WHENTHEN分开(我认为这在Java中是不可能的),但它提供了额外的好处,允许检查{{1}的特定部分},例如消息 1

我建议this article over at Baelung作为进一步阅读。


1 这在JUnit4中也是可能的,但是可以通过显式的Exception块或非常笨拙的机制来完成,该机制最终会破坏“ Given-When-Then”结构。有关更多信息,请查阅上面提到的Baelung上的文章。