PowerMock EasyMock Fundamentals

20,720

Solution 1

I think there are two issues here, one has to do with a missing call in your test code. The second has to do with your understanding of behavioral mocking and the difference between full and partial mocking.

Missing Calls

You seem to be missing a call to PowerMock.mockStatic or one of the PowerMock.mockPartialMock methods (see also this). The first method will mock all static methods passed to it, while the second will mock only the list of methods it's given.

An Example

Here's a complete example with two test methods that illustrate the two options. First, we have to annotate the test class using these two annotations:

@RunWith(PowerMockRunner.class)
@PrepareForTest(Dummy.class)
public class DummyTest {

The first annotation tells JUnit to run the test using PowerMockRunner. The second annotation tells PowerMock to prepare to mock the class Dummy.

Full Static Mock

Next, we'll look at the an example where all the static methods from class Dummy are mocked. What does this mean? It means essentially that we'll like to replace the actual implementation with fake ones (mocks). How should this fake implementation behave? This is what we specify in expectation.

Thus, in the testStaticMock method, we're telling EasyMock to give us a fake implementation of the two methods. We'll like the fake Dummy.testOuter to simply return true. And we'll like the fake Dummy.testInner to return true when passed the argument "bb". Note that when these mocks are activated (PowerMock.replayAll), the test code will only run the fake methods and not the actual implementation -- this appears to be the source of your confusion. I have more to say on that later.

    @Test
    public void testStaticMock() {
        mockStatic(Dummy.class);
        EasyMock.expect(Dummy.testOuter()).andReturn(true);
        EasyMock.expect(Dummy.testInner("bb")).andReturn(true);
        replayAll();
        boolean status = Dummy.testOuter();
        Assert.assertTrue(status);
        status = Dummy.testInner("bb");
        Assert.assertTrue(status);
        verifyAll();
    }

Partial Static Mock

Here is another test where we don't mock all the methods, only the ones that we pass to mockStaticPartial. Below, we are telling PowerMock that we only want to mock the method Dummy.testInner. Thus, part of Dummy is mocked and the rest is the class under test.

    @Test
    public void testPartialStaticMock() {
        mockStaticPartial(Dummy.class, "testInner");
        EasyMock.expect(Dummy.testInner("someValue")).andReturn(true);
        replayAll();
        boolean status = Dummy.testOuter();
        verifyAll();
        Assert.assertTrue(status);
    }
}

And let the mocked class Dummy be defined as follows:

public class Dummy {
    public static boolean testInner(String s) {
        return false;
    }

    public static boolean testOuter() {
        String x = "someValue";
        return testInner(x);
    }
}

Full mocking vs Partial Mocking

One thing to understand about full static mocking is that all the static methods are mocked. Thus, the method testOuter will be replace by a mocked version will have a implementation such as the following:

    public static boolean testOuter() {
        return true; //or whatever value is provided in the expectation
    }

Thus, we shouldn't expect the mocked version to invoke methods that the actual implementation does. We didn't want to care about the internals of the method. This was the reason, we decided to mock it anyway -- to replace its internals with a toy implementation, defined only by the expectation we set for it.

One the other hand, when we did partial mocking, we didn't mock testOuter, so we were invoking its actual implementation.

Solution 2

You're only telling EasyMock to expect a call to testInner(), not testOuter().

 Unexpected method call testOuter():

Tried this:

EasyMock.expect(C.testInner("blabla")).andReturn(true);
EasyMock.expect(C.testOuter());
PowerMock.replayAll();
Share:
20,720
phewataal
Author by

phewataal

Updated on July 22, 2022

Comments

  • phewataal
    phewataal almost 2 years

    This one is probably a PowerMock/EasyMock 101 question which I cannot figure out why. I have a class C with methods

    public static boolean testInner(String s) {
        return false;
    }
    
    public static boolean testOuter() {
        String x = "someValue";
        return testInner(x);
    }
    

    In my test of testOuter() method I want to ensure testInner is called with appropriate parameter. To do so, I am doing something like this: [@RunWith(PowerMockRunner.class) @PrepareForTest(EmailUtil.class) declared at Class level]

    EasyMock.expect(C.testInner("blabla")).andReturn(true);
    PowerMock.replayAll();
    boolean status = C.testOuter();
    PowerMock.verifyAll();  
    assertTrue(status);
    

    But I am getting error as:

    java.lang.AssertionError: 
    Unexpected method call testOuter():
    testInner("blabla"): expected: 1, actual: 0
        at org.easymock.internal.MockInvocationHandler.invoke(MockInvocationHandler.java:45)
        at org.powermock.api.easymock.internal.invocationcontrol.EasyMockMethodInvocationControl.invoke(EasyMockMethodInvocationControl.java:95)
        at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:105)
        at org.powermock.core.MockGateway.methodCall(MockGateway.java:60)
        at C.testOuter(C.java)
    

    I replaced the actual parameter with EasyMock.IsA(String.class) but still no luck. I am pretty sure I am doing something fundamentally silly here. Any help?