在不同的 JUnit 测试中管理检查的异常

Posted

技术标签:

【中文标题】在不同的 JUnit 测试中管理检查的异常【英文标题】:Managing checked exceptions in different JUnit tests 【发布时间】:2015-07-11 02:22:39 【问题描述】:

我正在为我的一种方法编写 Java 单元测试。方法声明是这样的:

public int convertToInteger() throws InvalidRomanNumberException

    int result=0;
    BaseRomanNumeral num1, num2; 
    int i=0;
    if(!validOperation())
        throw new InvalidRomanNumberException();

现在我正在尝试编写两个单元测试。一种是测试是否抛出了正确的异常。另一个是确保写入转换发生。这就是我的测试用例的样子

@Test
public void testRomanNumberConversion() 
    String romanValue="MCMII";
    RomanNumber num=new RomanNumber(romanValue);
    assertEquals(1903,num.convertToInteger());  


@Test(expected = InvalidRomanNumberException.class)
public void testInvalidRomanNumberExceptionThrown()  
    String romanValue="MCMIIII";
    RomanNumber num=new RomanNumber(romanValue);
    num.convertToInteger(); 

对于这两个测试用例,我都收到一条错误消息,提示 Unhandled InvalidRomanNumberException。仅当我将 throws InvalidRomanNumberException 添加到每个方法定义时才能解决此问题。但我认为这不是正确的方法。只是想和你们其他人核实一下,这里的标准是什么?我应该如何解决这个未处理的异常消息

【问题讨论】:

【参考方案1】:

因为看起来InvalidRomanNumberException 是一个检查异常,你必须用try-catch 包围它或者声明方法throws InvalidRomanNumberExceptionJUnit 与否,这是常态。

话虽如此,您expect 将抛出InvalidRomanNumberException 的测试用例方法理想情况下应该声明它throws 一个,因为没有必要用try-catch 抑制它,因为您的测试用例将失败。另一方面,您期望不会抛出异常的测试用例方法可以在convertToInteger 方法周围使用try-catch,并且无论是否抛出异常,该测试用例都应该在预期上具有assert来自convertToInteger 方法的结果。

JUnit 测试用例的最终结果应该是测试是通过还是失败。运行时的异常不会表明两者都没有。 JUnit 测试用例不能崩溃。

【讨论】:

所以你的建议是我只是添加一个 throws InvalidRomanNumberException 对。如果出现异常,则测试将失败。所以对于这个抛出异常的测试用例方法,不需要在程序的其他任何地方添加try catch @bob_d :不。我的建议是,您不希望出现异常的方法应该使用try-catch,而您确实希望出现异常的方法应该使用throws。在前一种情况下,无论是否抛出异常,您都应该断言您期望从 convertToInteger 方法获得的结果。在后面,您断言异常是expected。请参阅我对第二段最后几行的编辑。 投反对票的可以发表评论吗?不知道为什么一个接受的答案会导致否决票 没有理由在testRomanNumberConversion() 中添加try ... catch 块。最简单的认为可以工作的只是添加throws InvalidRomanNumberException。如果测试用例永远不会引发异常,则添加throws InvalidRomanNumberException 并没有什么坏处。如果测试抛出该异常——或任何异常——测试将失败,因为 JUnit 本身会捕获异常并将测试报告为失败(提供有用的失败堆栈跟踪!)。测试应该尽可能简单(但不要更简单)。 所以你投反对票是因为你不同意我的意见?您是否认为发表评论而不是投反对票会是更好的选择?请阅读:***.com/help/privileges/vote-down。我的回答是否草率、明显或危险地不正确?【参考方案2】:

这感觉更像是一个un检查异常而不是检查异常。

回想一下两者之间的区别:已检查异常意味着可以合理地从中恢复,例如丢失的文件或格式错误的 URL。未经检查/运行时异常意味着无法恢复,例如除以零。

如果用户输入了无效的罗马数字,说他们可以恢复并重试可能没有意义 - 转换 层不应该对此负责。这听起来更像是在实例化时决定的事情。

如果您改为将自定义异常扩展 RuntimeException,那么您不需要声明它被抛出(如果您这样做了,它不会有任何影响),并且您不必处理在你的测试中使用它。

替代方法是将其声明为在您的测试中抛出。这样做的好处是允许您将这些异常保持为已检查状态确保测试不会抱怨您未处理未捕获或未抛出的潜在异常。

@Test
public void testRomanNumberConversion() throws InvalidRomanNumberException 
    String romanValue = "MCMII";
    RomanNumber num = new RomanNumber(romanValue);
    assertEquals(1903, num.convertToInteger());  


@Test(expected = InvalidRomanNumberException.class)
public void testInvalidRomanNumberExceptionThrown() throws InvalidRomanNumberException 
    String romanValue = "MCMIIII";
    RomanNumber num = new RomanNumber(romanValue);
    num.convertToInteger(); 

【讨论】:

我不完全同意为所有测试用例方法声明throws 的建议。不期望异常的测试不应因为异常而在运行时导致编程异常。 JUnit 测试用例不应崩溃。它应该断言convertToInteger 方法的最终结果,抑制任何异常。这样一个测试用例的最终结果应该是测试是通过还是失败。运行时的异常不会表明两者都没有。 您好,感谢您的详细解释。它确实帮助我更好地理解检查和未经检查的异常 @ChetanKinger:我不一定不同意你的观点,因此我建议将其更改为未经检查的异常first。我的信念是确实需要检查实际上可恢复的异常,但在这种情况下,我觉得这种声明很好。抛出上述异常的唯一方法是输入无效的罗马数字,并且至少在第一个测试用例中不会发生。

以上是关于在不同的 JUnit 测试中管理检查的异常的主要内容,如果未能解决你的问题,请参考以下文章

JUnit:可能'期望'包装异常?

JUnit 5:如何断言抛出异常?

如何在junit中测试异常[重复]

使用JUnit测试预期异常

Junit - 期望异常测试(Expected Test)

JUnit 4 如何正确测试异常