在 JUnit 测试中尝试 catch

Try catch in a JUnit test

我正在为已经存在很长时间的应用程序编写单元测试。我需要测试的一些方法是这样构建的:

public void someMethod() throws Exception { 
   //do something 
}

如果我想测试这些方法,我必须在我的单元测试中写这样的东西:

@Test
public void someTest() {
   try {
      someMethod();
   }
   catch (Exception e) {
      e.printStackTrace();
   }
}

这样做是个好习惯吗?或者有其他方法可以测试这些方法吗?

我在互联网上做了一些研究,我找到了一些带有 @Rule 注释和 @Test(expected=Exception.class) 的解决方案,但是那不起作用(Eclipse 一直在 someMethod() 行中显示测试为错误)。 我不知道这些是否是好的解决方案,因为我对整个单元测试故事还很陌生。

如果有懂这方面的大神能帮帮我,感激不尽

由于 Exception 是一个已检查异常,您可以:

  • 必须在 try...catch 语句中捕获异常,或者
  • 在方法本身中声明要抛出的异常。

上面的内容工作正常,但我个人的偏好是声明要抛出的异常。这样,如果在测试 运行 期间抛出我不期望的异常,测试将 失败 .

@Test
public void someTest() throws Exception {
    // dodgy code here
}

如果我们需要查看是否抛出特定异常,那么您可以选择使用 @Rule 或直接将值添加到 @Test 注释。

@Test(expected = FileNotFoundException.class)
public void someTest() throws Exception {
    // dodgy code here
}

在 JUnit 5 中,您可以利用 Assertions.assertThrows 来完成同样的事情。我不太熟悉这个整体,因为它在编辑时还不是 GA,但它似乎接受来自 JUnit 5 的 Executable

@Test
public void someTest() {
    assertThrows(FileNotFoundException.class, () ->
         { dodgyService.breakableMethod() };
}
@Test
public void someTest() {
   try {
     someMethod();
   }
   catch (Exception e) {
     Assert.fail("Exception " + e);
   }
}

就是你能做的,如果不应该出现异常。另一种方法是像这样在签名中抛出异常:

@Test
public void someTest() throws Exception {
     someMethod();
}

不同之处在于,在一种情况下,测试将因断言异常而失败,而在另一种情况下,它将因测试崩溃而失败。 (就像在你的代码中的某处你得到一个 NPE 并且测试将因此)

你必须这样做的原因是因为 Exception 是一个已检查的异常。参见 Checked versus unchecked exception

@Test(expected=Exception.class) 用于测试,想要测试是否会抛出异常。

@Test(expected=ArrayIndexOutOfBounds.class)
public void testIndex() {
   int[] array = new int[0];
   int var = array[0]; //exception will be thrown here, but test will be green, because we expect this exception

}

不要在测试代码中捕获应用程序的异常。相反,声明它向上抛出。

因为,当 JUnit 的 TestRunner 发现抛出异常时,它会自动将其记录为测试用例的 error

只有当您 testcase 期望该方法应该抛出 Exception 时,您才应该使用 @Test(expected=Exception.class) 或捕获异常。

在其他情况下,只需将其向上抛出,

public void someTest() throws Exception {

您可以在测试方法签名中添加异常。然后,如果你正在测试是否抛出异常,你必须使用@Test(expected=Exception.class)。在不需要抛出异常的测试用例中,测试将成功通过。

@Test
public void testCaseWhereExceptionWontBeThrown() throws Exception {
    someMethod(); //Test pass
}

@Test(expected = Exception.class)
public void testCaseWhereExceptionWillBeThrown() throws Exception {
    someMethod(); //Test pass
}

关于 Junit 测试人员如何处理异常有两个主要规则:

  1. 如果异常源自测试代码:

    • 如果是预期的,请在 Test 注释的 expected 属性中声明它。或者,如果应该对异常对象本身进行进一步检查,则捕获并忽略它。 (在这种情况下,还必须在 try 块的末尾调用 Assert.fail,以指示未产生预期的异常)。
    • 如果不是预期的,抓住它并执行Assert.fail。 (之前调用 Exception.printStackTrace 也很有用)。
  2. 如果异常不是起源于被测试的代码,或者它对测试不感兴趣(例如,大多数 IOExceptions 是在网络级别产生的,在测试之前 even be completed), 在throws 子句重新抛出。

为什么你应该期待测试器中出现异常?提醒:您应该为测试代码上的每个可能结果编写一个测试方法(以实现高代码覆盖率):在您的情况下,一个方法必须return成功,至少另一个必须产生异常。

这是什么异常?是吗

  1. 在您的单元测试或
  2. 中使用不会发生的流等操作的异常
  3. 由于某种错误的输入而可能发生的异常?

如果是 1。我会把它放在方法签名级别,因为 try-catch 除了仪式之外没有任何实际用途。

@Test
public void testFoo() throws Exception {
    // ...
}

如果是2.那就有点复杂了。如果抛出异常,您需要问自己应该发生什么。测试应该失败吗?是预期的吗?这无关紧要吗?以下示例说明了如何处理所有这些。 注意: 我只使用了 Exception,因为你使用了。我希望它真的不是,因为如果有可能抛出某些 other 异常,而不是预期的异常,那么这些异常将非常不稳定。如果可能,不要使用 Exception,使用更具体的东西(在 junit 代码中)。

// The below code assumes you've imported the org.junit.Assert class.

@Test
public void thisShouldFailIfExceptionCaught() {
    //Given...
    try {
        // When...
    } catch (Exception e) {
        Assert.fail();
    }
    // Then...
}

@Test
public void thisShouldPassOnlyIfTheExceptionIsCaught() {
    //Given...
    try {
        // When...
        Assert.fail();
    } catch (Exception expected) {}
    // No "then" needed, the fact that it didn't fail is enough.
}

@Test
public void irrelevantExceptionThatCouldBeThrown() {
    //Given...
    try {
        // When...
    } catch (Exception e) {}
    // Then...
}

关于JUnit的三点:

  • 测试应该精确,它们应该仅根据测试输入的设置方式明确地通过或失败。

  • 测试应该将失败报告回框架。

  • 测试不应依赖于读取其输出。

您的示例在所有三个方面均失败。无论是否抛出异常,测试仍然通过。如果抛出异常,JUnit 永远不会发现它并且不能将它包含在测试结果中。知道出现问题的唯一方法是读取测试写入标准输出的内容,这使得错误很容易被忽略。这不是编写测试的有用方法。

JUnit 旨在让做正确的事变得容易,并为开发人员提供有用的反馈。如果从测试方法中抛出异常,它就会被框架捕获。如果测试被注释为异常,表明异常是预期的,那么框架将测试标记为通过。否则,框架将无法通过测试并记录堆栈跟踪以进行报告。该框架报告哪些断言失败以及发生了哪些意外异常,以便每个人都知道测试是否有效。

如果您希望测试成功而不抛出异常,那么如果测试中有任何内容可以抛出已检查的异常,请将 throws Exception 添加到测试方法签名中。将 throws 添加到签名并不表示方法 必须 抛出任何东西,它只是让任何碰巧发生的异常被抛出,以便测试框架可以捕获它们.

您在测试中实际捕获异常的唯一实例是您要测试有关异常的断言的地方;例如,您可以测试异常消息是否符合您的预期,或者异常是否设置了原因。在这种情况下,您将在 try 块的末尾添加 Assert.fail(),这样没有抛出异常将导致测试失败。

糟糕的不是 try-catch 块,而是缺少任何会导致测试失败的东西。

当你一开始写测试的时候,让它失败。这样你就可以向自己证明你知道测试在做什么,并且你确认,当出现故障时,你会被告知。