【问题标题】:How can my @Rule work together ExpectedException rule?我的 @Rule 如何与 ExpectedException 规则一起工作?
【发布时间】:2015-03-07 05:34:59
【问题描述】:

我创建了一个类似于@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,那么异常不起作用?那么为什么?如何让它仍然工作?

【问题讨论】:

    标签: junit rule


    【解决方案1】:

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

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

    JUnit 有一个内部假设,即为每个测试方法创建一个新的测试类实例。 JUnit 这样做是为了让一个测试方法运行时存储在测试实例中的状态不会影响下一个测试方法运行。

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

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

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

    1. 创建测试类的实例。
    2. 创建一个将运行测试方法的Statement
    3. 如果方法的@Test 注释使用expected 属性,则用另一个处理预期异常的语句包装Statement
    4. 如果方法的@Test 注释使用timeout 属性,则用另一个处理超时的语句包装Statement
    5. 用其他语句包装Statement,这些语句将调用用@Before@After 注释的方法
    6. Statement 与将调用规则的其他语句包装在一起

    更多详情请关注the JUnit source

    因此,传递给您的规则的 Statement 包装了一个已经构建的类(它必须如此,因此您的规则的 apply() 方法被调用)。

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

    如果您不能依赖 JUnit 4.12,您可以通过使用 RuleChain 规则来破解它以确保您用于多次运行测试的自定义规则“围绕”其他规则运行。

    【讨论】:

    • 感谢 NamshubWriter。什么是“找到一种方法来用规则或基本测试类替换您的自定义运行器”,请注意我的客户规则已经是一个规则。它不是跑步者。
    • 顺便说一句,即使我在参数列表中定义了一个参数,测试也会失败,因此即使不重复测试也会出现问题
    • @user389955 那么也许仅仅使用 RuleChain 会有所帮助。如果没有,您能否分享您的自定义规则的代码?
    • @user389955 我的意思是如果你不能使用Parameterized 因为你需要一个不同的自定义运行器,那么你可以用规则替换 那个 自定义运行器。但如果您使用 JUnit 4.12,则有更好的选择。我更新了我的答案。
    • NamshubWrite。得到。谢谢。 1)这意味着规则没有我学到的那么强大。在很多情况下它不支持多个规则。至少它不适用于 Rule ExpectedException。 2) 我不想将我的新逻辑附加到现有的 RunWith 中。 3) 我会试试 RuleChain。
    【解决方案2】:

    如果您使用的是 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());
    }
    

    【讨论】:

    • 我没有使用 java 8。:-( 感谢您的回复
    猜你喜欢
    • 2019-04-13
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多