Powermock: Mocking a method on a class results in the method being invoked prior to it being mocked.

Created on 6 Jun 2018  路  6Comments  路  Source: powermock/powermock

We have been using Powermock with Mockito to mock and spy on various methods. I observe that, when using spy to partially mock a class, when doing the actual mocking, the method being mocked is first called, leading to exceptions and errors.

com.full.mockito.StaticTest.java:

package com.full.mockito;

import static org.powermock.api.mockito.PowerMockito.spy;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.powermock.api.mockito.PowerMockito.whenNew;
import static org.powermock.api.mockito.PowerMockito.mock;

import org.powermock.api.mockito.PowerMockito;

import java.io.IOException;

import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;


@RunWith(PowerMockRunner.class)
@PrepareForTest(fullyQualifiedNames = "com.full.mockito.*")
public class StaticTest 
    extends TestCase
{

    /**
     * Create the test case
     *
     * @param testName name of the test case
     */
    public StaticTest( String testName )
    {
        super( testName );
    }

    /**
     * @return the suite of tests being tested
     */
    public static Test suite()
    {
        return new TestSuite( AppTest.class );
    }


    public void testStaticMethodMocking() throws Exception {

            String expectedResult = "This is a static test.";
            spy(StaticClass.class);

            //PowerMockito.mockStatic(StaticClass.class);
            System.out.println("About to **set up mock** test code...");
            when(StaticClass.class, "getStaticData", "test").thenReturn("This is a static test.");

            //when(StaticClass.getStaticData("test")).thenReturn("This is a static test.");
            //PowerMockito.when(StaticClass.getStaticData("test")).thenReturn("This is a static test.");

            System.out.println("\nAbout to **run** test code...");
            String actualResult = StaticClass.getStaticData("test");

            assertEquals(expectedResult, actualResult);

            System.out.println("\nCheck the default behavior exists for other input...");
            assertEquals("This is the REAL static response.", StaticClass.getStaticData("something"));
    }
}

com.full.mockito.StaticClass.java:

        public static String getStaticData(String input)  {
        System.out.println("The method we're trying to stub RAN! " + input);
        return "This is the REAL static response.";
    }

The output is as follows:

About to **set up mock** test code...
The method we're trying to stub RAN! test

About to **run** test code...

Check the default behavior exists for other input...
The method we're trying to stub RAN! something

You can see that in the first output block, the getStaticData method runs with the input String "test". It appears to be doing this in the "when" block. We're not wanting to call the method here. We are wanting to MOCK the method here.

Later, in the second output block, we see that the call to getStaticData with input String "test" results in the mocked method being run. We don't see the System.out.println statement. This works as expected.

Finally, in the last block, we see the println statement in getStaticData because we passed in an input String for which we did not mock.

The problem, to be clear, is in the second output block. Before we've mocked a specific method, we don't want to invoke it, yet the act of calling:

when(StaticClass.class, "getStaticData", "test").thenReturn("This is a static test.");

Appears to actually INVOKE the method instead of only MOCK the method.

This works if we create a mock, but what about cases where we want all of the other functionality in the class to continue to function correctly? This appears to be a bug. It happens regardless of whether we are spying and mocking a Static class or a non-Static class.

Mockito version: 1.10.19
PowerMockito version: 1.7.4

question

Most helpful comment

It's a correct behaviour. When you call a method on spy then a real method is called until the method is mocked. When you start mocking method Mockito does not know if the method should be mocked or it's just calling a method within a test.
To avoid such behaviour you need use doReturn. See example here

All 6 comments

You can also clone this if you want to check it out: https://github.com/FullLearning/PowerMockitoExample/tree/mocking-class-created-in-outer-class

Run the StaticTest class with JUnit.

Any update on this issue? Thank you.

It's a correct behaviour. When you call a method on spy then a real method is called until the method is mocked. When you start mocking method Mockito does not know if the method should be mocked or it's just calling a method within a test.
To avoid such behaviour you need use doReturn. See example here

The difference between your example and this is that _I_ am not invoking the method. PowerMock is invoking the method. I'm merely passing in the method name as a string argument:

when(StaticClass.class, "getStaticData", "test").thenReturn("This is a static test.");

While your suggestion to use doReturn did indeed work, I don't get why the above statement actually _calls_ the getStaticData method right before mocking it. What's a use case where someone would want to mock something, but invoke the original method before mocking it? Because that's what happens here. If I then try to invoke getStaticData after this statement, it doesn't call it and instead returns "This is a static test".

PowerMock invokes method for you in this case. PowerMock does not mock anything but itself. It uses an engine of underling mock framework, in your case Mockito. This statement just an easy way to call a non-public method for Mockito
when(StaticClass.class, "getStaticData", "test").thenReturn("This is a static test.");

What's a use case where someone would want to mock something, but invoke the original method before mocking it?

It's not a use case. It's a technical limitation of Java, API and Spy object. Then you have
when(spy.get(0)).thenReturn("foo");
the method get is called first. As it's a spy then mocking engine cannot understand if it's mocking or calling a method from SUT. As result, the real method is called to keep spy behaviour.

So the spy.get(0) is called at 'when time.' Thought it was called at assert or verify time only.

Was this page helpful?
0 / 5 - 0 ratings