将 PoweMock 与 PowerMockRule 一起使用时,使用 ExpectedException 进行的测试失败

Test with ExpectedException fails when using PoweMock with PowerMockRule

我正在尝试使用 PowerMock,而不是 Mockito;因为我喜欢 API 的 whennew() 和 verifyprivate() 但是我在 Junit 中尝试 运行 带有类别 TestRunner 的测试套件时遇到了一些问题。

为了使用默认的 JUnit 测试 运行 用户,我创建了一个 TestCase 并添加了 PowerMockRule 作为带有 @Rule 注释的实例字段。虽然测试的执行是这样进行的,但 ExpectedException TestRule 在与

结合使用时不起作用

示例代码

@PowerMockIgnore ("*")
@PrepareForTest (CustomizedSSHConnection.class)
public class TestExpectedExceptionRule {

    private Connection          connection;
    private ConnectionInfo      connectionInfo;
     @Rule
     public PowerMockRule rule = new PowerMockRule ();
    @Rule
    public ExpectedException    exception   = ExpectedException.none ();

    @Test
    public void testExcepitonWithPowerMockRule() {
        exception.expect (NullPointerException.class);
        exception.expectMessage ("Image is null");
        throw new NullPointerException ("Image is null");
    }
}

如果我使用@RunWith(PowerMockRunner.class)而不是使用@Rule PowerMockRule,这个测试用例将通过。

另一个观察结果是,如果我用 @ClassRule 注释 PowerMockRule,这会成功,但一些模拟方法会抛出异常。

我能够使用 @Test 注释中的预期属性修复此问题。但这种方法的问题是无法断言异常消息。现在对我来说没问题。

@PowerMockIgnore ("*")
@PrepareForTest (CustomizedSSHConnection.class)
public class TestExpectedExceptionRule {

    private Connection          connection;
    private ConnectionInfo      connectionInfo;
     @Rule
     public PowerMockRule rule = new PowerMockRule ();
    @Rule
    public ExpectedException    exception   = ExpectedException.none ();

    @Test(expected = NullPointerException.class)
    public void testExcepitonWithPowerMockRule() {
        throw new NullPointerException ("Image is null");
    }
}

PowerMock 创建 TestExpectedExceptionRule 对象的深度克隆。因此,它是 运行 具有新 ExpectedException 规则的测试,但您在原始规则上调用 exception.expect (NullPointerException.class)。因此测试失败,因为 ExpectedException 规则的克隆不期望异常。

尽管如此,您的问题至少有两种解决方案。

规则链

使用 JUnit 的 RuleChain 对规则进行排序。这需要一些额外的丑陋代码,但它可以工作。

private ExpectedException exception = ExpectedException.none ();
private PowerMockRule powerMockRule = new PowerMockRule();

@Rule
public TestRule ruleChain = RuleChain.outerRule(new TestRule() {
    @Override
    public Statement apply(Statement base, Description description) {
        return powerMockRule.apply(base, null, description);
    }
}).around(exception);

鱼缸

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

@Test
public void testExcepitonWithPowerMockRule() {
  Throwable exception = exceptionThrownBy(
    () -> throw new NullPointerException ("Image is null"));
  assertEquals(NullPointerException.class, exception.getClass());
  assertEquals("Image is null", exception.getMessage());
}

没有Java8,你必须使用匿名class。

@Test
public void fooTest() {
  Throwable exception = exceptionThrownBy(new Statement() {
    public void evaluate() throws Throwable {
      throw new NullPointerException ("Image is null");
    }
  });
  assertEquals(NullPointerException.class, exception.getClass());
  assertEquals("Image is null", exception.getMessage());
}

我通过创建一个使用 FunctionalInterface.

PowerMockTestUtil class 解决了这个问题

效用 class:

/**
 * Utility class to provide some testing functionality that doesn't play well with Powermock out
 * of the box. For example, @Rule doesn't work well with Powermock.
 */
public class PowerMockTestUtil {

  public static void expectException(RunnableWithExceptions function, Class expectedClass, String expectedMessage) {
    try {
      function.run();
      fail("Test did not generate expected exception of type " + expectedClass.getSimpleName());
    } catch (Exception e) {
      assertTrue(e.getClass().isAssignableFrom(expectedClass));
      assertEquals(expectedMessage, e.getMessage());
    }
  }

  @FunctionalInterface
  public interface RunnableWithExceptions<E extends Exception> {
    void run() throws E;
  }
}

样本测试:

  @Test
  public void testValidateMissingQuantityForNewItem() throws Exception {
    ...
    expectException(() -> catalogEntryAssociationImporter.validate(line),
        ImportValidationException.class,
        "Quantity is required for new associations");
  }