【问题标题】:Testing for multiple exceptions with JUnit 4 annotations使用 JUnit 4 注释测试多个异常
【发布时间】:2010-11-27 11:11:25
【问题描述】:

是否可以在单个 JUnit 单元测试中测试多个异常?我知道一个可以使用的例外,例如

    @Test(expected=IllegalStateException.class)

现在,如果我想测试另一个异常(例如 NullPointerException),可以在同一个注解中完成,还是在不同的注解中完成,还是我需要完全编写另一个单元测试?

【问题讨论】:

  • 如果有多个异常表明测试代码按预期工作,那么测试定义不明确。您应该从测试中得到一件事。

标签: java junit exception


【解决方案1】:

你真的希望测试做一个的事情,并为此进行测试。如果您不确定要抛出哪个异常,那听起来对我来说不是一个好的测试。

例如(伪代码)

try {
   badOperation();
   /// looks like we succeeded. Not good! Fail the test
   fail();
}
catch (ExpectedException e) {
   // that's fine
}
catch (UnexpectedException e) {
   // that's NOT fine. Fail the test
}

因此,如果您想测试您的方法是否抛出 2 个不同的异常(针对 2 组输入),那么您需要 2 个测试。

【讨论】:

  • 我通常在 try 块中调用 fail(),紧跟在 badOperation() 之后。
  • 在任何给定的点上,我都确定我希望哪个语句会引发错误。在一项测试中,我希望测试是否抛出了所有预期的异常。老实说,我对测试异常的注释方式并不感到兴奋:一个糟糕的测试可能会在测试中的意外点导致预期的异常。从这个意义上说,我赞成 try-catch-fail 测试异常,但看起来很丑。不过,我想你在某种意义上是对的:我的测试应该更加原子化(而不是像我试图做的那样在单个单元测试中测试一种方法的所有内容)。
  • @Yuval - 在上面我试图成为测试框架不可知论者。但是我已经修改得更清楚了。
  • @Phantom - 测试的原子性可能会很痛苦,但我相信这是前进的方向,并在测试失败时减少歧义。我已经编写了检查多个异常的测试,尽管在前面测试的 catch 块中不使用注释和嵌套测试。一段时间后,它会变得丑陋...
  • “catch (UnexpectedOperation e)”是不必要的;如果测试方法抛出异常,则视为失败。正如 Yuval 提到的,您需要在 badOperation() 之后立即执行 fail()
【解决方案2】:

注释无法做到这一点。

在 JUnit 4.7 中,您可以使用新的 ExpectedException 规则

public static class HasExpectedException {
    @Interceptor
    public ExpectedException thrown= new ExpectedException();

    @Test
    public void throwsNothing() {
    }

    @Test
    public void throwsNullPointerException() {
         thrown.expect(NullPointerException.class);
         throw new NullPointerException();
    }

    @Test
    public void throwsNullPointerExceptionWithMessage() {
        thrown.expect(NullPointerException.class);
        thrown.expectMessage("happened?");
        throw new NullPointerException("What happened?");
    }
}

更多查看


如果您无法更新到 JUnit 4.7,则必须编写表单的裸单元测试

public test() {
    try {
        methodCall(); // should throw Exception
        fail();
    }
    catch (Exception ex) {
        assert((ex instanceof A) || (ex instanceof B) || ...etc...);
        ...
    }

}

【讨论】:

  • 我有一个案例,我必须测试异常类列表中的一个。如果您在代码中使用@Nonnull 注释,则从 IDE 运行的单元测试可能会引发 InvalidArgumentException,但如果您从构建(即 maven、gradle)运行测试,您仍然必须测试 null 并且可以响应另一种类型的异常。以下内容涵盖了这两种情况:expected.expect(CoreMatchers.anyOf(CoreMatchers.instanceOf(IllegalArgumentException.class), CoreMatchers.instanceOf(MyCustomInvalidFieldValueException.class)));
【解决方案3】:

虽然这在 JUnit 4 中是不可能的,但如果您切换到允许您编写的 TestNG,是可能的

@Test(expectedExceptions = {IllegalArgumentException.class, NullPointerException.class})

【讨论】:

  • 不知道这个。酷。
【解决方案4】:

使用catch-exception:

// test 
public void testDo() {

   // obj.do(1) must throw either A or B
   catchException(obj).do(1);
   assert caughtException() instanceof A
       || caughtException() instanceof B;

   // obj.do(2) must throw A but not SubclassOfA
   catchException(obj).do(2);
   assert caughtException() instanceof A
       && !(caughtException() instanceof SubclassOfA);

}

【讨论】:

    【解决方案5】:
    @Test(expected=Exception.class)
    

    这将抛出所有可能的异常。

    【讨论】:

    • 它期望所有异常都不会抛出它们。
    【解决方案6】:

    您希望“预期”如何工作?一个方法只能抛出一个异常。

    您必须为方法可能失败的每种方式编写不同的单元测试。因此,如果该方法合法地抛出了两个异常,那么您需要设置两个测试来强制该方法抛出每个异常。

    【讨论】:

    • 一个方法只能抛出一个异常,但可以声明为抛出许多不同的异常。假设一个方法可以抛出 4 种异常类型,抛出 A 或 B 通过测试,抛出 C 或 D 未通过测试。这很容易是“预期”工具一个类数组而不是单个类
    • 但是在一组数据下只能以一种方式失败。
    【解决方案7】:

    让测试尽可能简单和简短。 JUnit-Test 的目的是只测试一种简单的功能或一种单一的故障方式。

    确实,为了安全起见,您应该为每种可能的执行方式创建至少一个测试。

    通常情况下,这并不总是可行的,因为如果您有一个分析字符串的方法,那么可能的字符串组合太多,您无法涵盖所有​​内容。

    保持简短。

    您可以轻松地为一种方法设置 30-40 种测试方法……这真的很重要吗?

    问候

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-28
      • 1970-01-01
      相关资源
      最近更新 更多