Sharath Bhaskara
Sharath Bhaskara

Reputation: 497

Test with ExpectedException fails when using PoweMock with PowerMockRule

I am trying to work with PowerMock, over Mockito; as I loved the API's for whennew() and verifyprivate() but i have some problem when trying to run testsuites with Categories TestRunner in Junit.

For using default JUnit test runners, I created a TestCase and added PowerMockRule as instance field with @Rule annotation. While execution of tests worked like this, ExpectedException TestRule is not working when used in conjunction

Example Code

@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");
    }
}

Instead of using @Rule PowerMockRule if I use @RunWith(PowerMockRunner.class) this testcase will pass.

One other observation is if I annotate PowerMockRule with @ClassRule this succeeds but some of the mocking methods throwing exceptions.

Upvotes: 1

Views: 5899

Answers (3)

Mike Wilklow
Mike Wilklow

Reputation: 128

I solved this problem by creating a PowerMockTestUtil class that uses a FunctionalInterface.

Utility 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;
  }
}

Sample test:

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

Upvotes: 0

Sharath Bhaskara
Sharath Bhaskara

Reputation: 497

I was able to fix this using the expected attribute in the @Test annotation. But the problem with this approach is that am unable to assert the exception message. Which is fine for me for now.

@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");
    }
}

Upvotes: 0

Stefan Birkner
Stefan Birkner

Reputation: 24540

PowerMock creates a deep clone of the TestExpectedExceptionRule object. Because of this it is running the test with a new ExpectedException rule, but you're calling exception.expect (NullPointerException.class) on the original rule. Hence the test fails, because the clone of the ExpectedException rule doesn't expect an exception.

Nevertheless there are at least two solutions for your problem.

RuleChain

Order the rules with JUnit's RuleChain. This needs some additional ugly code, but it works.

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);

Fishbowl

If you are using Java 8 then you can replace the ExpectedException rule with the Fishbowl library.

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

Without Java 8, you have to use an anonymous 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());
}

Upvotes: 2

Related Questions