Java单元测试中出现意外异常

时间:2013-05-12 14:00:10

标签: java unit-testing exception junit

我真的是JUnit的新手和一般的单元测试,我很难找到合适的方法。处理意外异常的更好方法是什么?为什么?

方法A:

  1. 首先抓住预期的,但没有通过消息进行测试
  2. 在最后一个catch块中,捕获一般异常并使测试失败并显示“发生意外异常”消息
  3. 方法B:

    1. 仅捕获预期的消息,但消息显示
    2. 使用throws Exception标记测试方法,让任何意外的“冒泡”完全脱离测试
    3. 为了增加混乱,通过说“意外异常”,我指的是其中一个:

      1. 正在测试的方法声称要抛出的异常,但在这种情况下。例如,我的方法抛出IllegalArgumentException和NullPointerException,但是在这个try块中,我希望它抛出IllegalArgument; NullPointer被认为是意外的。赶上或失败或冒泡?
      2. 我真的无法预料的一般例外
      3. 我知道这个问题有点令人困惑,我自己迷失了,但希望有人会给我任何暗示。不会因为downvotes而责怪你,它仍然值得冒险:)

5 个答案:

答案 0 :(得分:3)

使用JUnit的测试方法,基本上,如果方法设法无错误地结束,JUnit会认为测试已通过。所以你应该做的是捕获应该到来的所有异常。在这种情况下,这是你可以做的:

@Test
public myMethodToTest()
{
    try {
        /* ... */
        /* error should trigger before the following statement : */
        fail("Reason");
    } catch(GoodException e) { } 
   // no need to do anything since you expected that error
   // You can let the test ends without further statement 
}

There is another写它的方式:

@Test (expected=IndexOutOfBoundsException.class)
public void elementAt()
{
    int[] intArray = new int[10];

    int i = intArray[20]; // Should throw IndexOutOfBoundsException
}

如果您仍然想要,您仍然可以捕获不应触发的异常,并执行类似的操作:

@Test
public myMethodToTest()
{
    try {
        /* ... */
    } catch(BadException e) {
        fail("Reason");
    }

}

答案 1 :(得分:2)

您可以使用此语法,请参阅SO问题(Asserting exceptions in Java, how?

@Test(expected=NumberFormatException.class);

确保可能抛出预期的异常。

某些方法可能合法地抛出多个异常。例如,如果您正在求解二次方程,则可以预期除以零和负平方根。您应该通过编写两个单独的单元测试来测试每个单元测试,每个单元测试都设计为失败,因此测试抛出异常。

示例:

Pair roots = solveQuadratic(double a, double b, double c) {
    //... 
}

其中Pair是预期的一对解决方案。

这是我们希望通过的测试。

@Test
public void testQuadratic() {
    Pair roots = solveQuadratic(1.0, -5.0, 6.0);
    Assert.assertEqual(3.0, roots.largest, 0.000001); 
    Assert.assertEqual(2.0, roots.smallest, 0.000001); 
}

这是我们期望失败的一个:

@Test
public void testQuadraticDivideByZero() {
    try {
        Pair roots = solveQuadratic(0.0, -5.0, 6.0);
        Assert.fail("should have failed with ArithmeticException");
    } catch (ArithmeticException e) {
    }
}

在这种情况下,您可能希望编写代码以便捕获a=0.0并使用消息抛出IllegalArgumentException。

一般来说,你应该想到所有可能出错的东西并将它们捕获并测试陷阱。因为有一天某人传递给a=0.0此方法,并且需要明确的错误消息。

答案 2 :(得分:1)

UNIT测试方法的想法是确保它是一种可靠的方法。可靠性意味着对于给定的输入,输出始终是预期的输出。因此,它取决于方法的预期是否存在异常。如果我们认为发生了意外的异常并且预计方法无法对其进行任何操作,那么您可以将测试用例标记为PASSED。但是,如果期望该方法处理任何异常情况并且如果不能,则将测试用例标记为FAILED。

答案 3 :(得分:1)

拥有测试用例的想法是避免所有意外异常,方法是输入所有边界条件

如果该方法抛出一些Exceptions(该方法具有throws子句并且它尝试通过一些已检查的异常通知调用者,则传递使您的测试用例抛出的输入那个例外,在你的测试用例中catch。这使得其中一个测试用例 PASS

如果该方法引发意外异常,修复方法。方法应该足够稳定抛出意外异常,例如NullPointerException,并且应该修复。

因此,要回答您的问题,处理意外异常的更好方法是找到它们,方法是增强您修复它的测试用例。

答案 4 :(得分:1)

我有种感觉,到目前为止,没有一个答案能够解决问题。 OP明确要求处理 un 预期的异常。我在这个主题上的两分钱是:

这取决于您想要达到的详细程度:

通常,您应该努力进行简短的测试,以确定代码的一个方面。理想情况下,只需调用少数方法,其中只有一个或两个可能引发意外异常。在这种情况下,为所有已检查的异常添加 throws -clause应足以在测试失败时分析问题。我更喜欢这种解决方案,因为它更容易编写,更短更全面

示例:

@Test
public void testPrivateMethod() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {

    //...

    Method method = MyClass.class.getMethod("privateMethod");
    method.setAccessible(true);
    boolean result = method.invoke(myInstance);
    assertTrue(result);
}

如果测试代码需要更复杂,则可能有多种方法可能会引发异常。可能有些人甚至会抛出同类异常。在这种情况下,try-catch块可能有助于在测试用例失败时定位问题。但是,此会生成更多代码,并且可能会使测试的可读性降低

示例:

@Test
public void testPrivateMethod() {

    //...

    Method method;
    try {
        Method method = MyClass.class.getMethod("privateMethod");
        method.setAccessible(true);
        boolean result = method.invoke(myInstance);
        assertTrue(result);
    } catch (NoSuchMethodException | SecurityException e) {
        fail("Could not access method 'privateMethod'.");
    } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
        fail("Call to 'privateMethod' raised an exception.")
    }
}

我希望我有正确的意图。