我的@Rule如何与ExpectedException规则一起工作?

时间:2015-03-04 03:16:05

标签: junit rule

我创建了一个类似于@RunWith parameterized.class的@Rule,这样所有的测试用例都可以用不同的参数重复测试(我不能直接使用parameterized.class,因为我的测试类已经将@Runwith用于其他目的)。

但是,在测试以下方法时@Rule不起作用:

@Test
public void fooTest() {
/*exception is an ExceptedException initialized somewhere else as:
 *@Rule public ExpectedException exception = ExpectedException.none();
 */
    exception.expect(A);
    exception.expectMessage(B);
    someTestWhichThrowExceptionOfB(); 
}

事实上,如果我将我的参数硬编码为一个值,那么测试就会通过,因为它会抛出B的异常。 但是,如果我设置我的参数= MyParameterRule.value(),测试确实会抛出B的异常,但然后失败并说它失败,因为B的异常?

我想在第二种情况下,如果我使用MyParameterRule,那么异常不起作用?那么为什么?如何使它仍然工作?

2 个答案:

答案 0 :(得分:3)

如果您可以依赖JUnit 4.12,则可以将Parameterized@UseParametersRunnerFactory一起使用(有关详细信息,请参阅the Parameterized Javadoc)。

至于为什么你的参数化规则不起作用,这是一个(有点长)的解释。

JUnit有一个内部假设,即为每个测试方法创建一个新的测试类实例。 JUnit执行此操作,因此从一个测试方法运行中存储在测试实例中的状态不会影响下一个测试方法运行。

ExpectedException规则具有相同的期望。当您调用expect时,它会更改在该字段初始化时创建的ExpectedException字段的状态。它将其修改为“期望”抛出异常。

当您的规则尝试运行相同的方法两次(使用不同的参数)时违反此假设。第一次调用调用expect的方法时,它会起作用,但是当您再次调用它时,它可能无效,因为您正在重新使用以前修改过的ExpectedException规则。

当JUnit运行JUnit4样式测试的测试方法时,它会执行以下操作:

  1. 创建测试类的实例。
  2. 创建将运行测试方法的Statement
  3. 如果方法的@Test注释使用expected属性,请用另一个处理预期异常的语句包装Statement
  4. 如果方法的@Test注释使用timeout属性,请用另一个处理超时的语句包装Statement
  5. Statement与其他语句一起换算,这些语句将调用使用@Before@After
  6. 注释的方法
  7. Statement与其他将调用规则的语句包装在一起
  8. 有关详细信息,请查看the JUnit source

    因此传递给规则的Statement包装了一个已构造的类(必须这样,因此调用规则的apply()方法)。

    不幸的是,这意味着不应该使用Rule多次运行测试方法,因为测试(或它的规则)可能具有上次运行方法时设置的状态。

    如果你不能依赖JUnit 4.12,你可以通过使用RuleChain规则来破解这个工作,以确保你用来多次运行测试的自定义规则“围绕”另一个运行规则。

答案 1 :(得分:0)

如果您使用的是Java 8,则可以将ExpectedException规则替换为Fishbowl库。

@Test
public void fooTest() {
  Throwable exception = exceptionThrownBy(
    () -> someTestWhichThrowExceptionOfB());
  assertEquals(A.class, exception.getClass());
  assertEquals("B", exception.getMessage());
}

这也可以通过Java 6和7中的匿名类来实现。

@Test
public void fooTest() {
  Throwable exception = exceptionThrownBy(new Statement() {
    public void evaluate() throws Throwable {
      someTestWhichThrowExceptionOfB();
    }
  });
  assertEquals(A.class, exception.getClass());
  assertEquals("B", exception.getMessage());
}