Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock both static and non static methods in a same class using powerMock?

I have a simple case to illustrate a much more complicated one (oh Legacy Code, do I love you, shall minstrels sing marvellous songs in thy name).

Picture a set of class as the following :

  • The utility class :
package org.osef.test;

public final class A {

    private static A instance;
    public static String status;

    private A() {
        initPaths();
    }

    public static A getInstance(){
            if(instance==null){
                instance = new A();
            }
            return instance;
    }

    private void initPaths() {
        A.status = "I have been in the method !";
    }
    public String doStuff() {
        return "stuff done ...";
    }
}
  • the class calling it
package org.osef.test;

public class B {

    public String doBdo() {
        A instance = A.getInstance();
        return instance.doStuff();
    }
}
  • the class testing this pile of sh... ahem ... shtrongly difficult piece of "logic".

package org.osef.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;

import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;

@RunWith(PowerMockRunner.class)
@PrepareForTest({ A.class })
public class BTest {

    @Before
    public void setUp() {
        PowerMock.replace(PowerMock.method(A.class, "getInstance")).with(PowerMock.method(BTest.class, "giveOutInstance"));

        A a = A.getInstance();

        EasyMock.expect(a.doStuff()).andReturn("lol");
        EasyMock.replay(a);
    }

    @Test
    public void testDoBdo() {

        B b = new B();
        assertEquals("lol", b.doBdo());
        assertNull(A.status);
    }

    public static A giveOutInstance(){
        return Whitebox.newInstance(A.class);
    }
}
  • and another approach had already been to go as follows :
package org.osef.test;

//[imports ommited here but are the same that those of the previous example]

@RunWith(PowerMockRunner.class)
@PrepareForTest({ A.class })
public class BTest {

    @Before
    public void setUp() {
        PowerMock.mockStatic(A.class);
        A a = Whitebox.newInstance(A.class);
        EasyMock.expect(A.getInstance()).andReturn(a);
        PowerMock.replay(A.class);

        EasyMock.expect(a.doStuff()).andReturn("lol");

        EasyMock.replay(a);
    }

    @Test
    public void testDoBdo() {

        B b = new B();
        assertEquals("lol", b.doBdo());
        assertNull(A.status);
    }

}

But in all cases I get :

java.lang.IllegalStateException: no last call on a mock available at org.easymock.EasyMock.getControlForLastCall(EasyMock.java:560) at org.easymock.EasyMock.expect(EasyMock.java:538) at org.osef.test.BTest.setUp(BTest.java:25) ...

  • I just need to test that final class A.
  • I need to avoid it's constructor logic (monstruous and irrelevant in my testing of the doStuff" method).
  • I have to test that doStuff.

Any idea how to do what I want to do effectively ?

like image 272
Ar3s Avatar asked Sep 30 '22 10:09

Ar3s


1 Answers

I think your issue is that you're not replaying the classes, you're only replaying the mock instance of A in each test.

PowerMock.replayAll() is your friend here. It will force the classes you've got expectations on to be in replay mode, then your mock instance can be returned by the invocation of the static method.

Here is a sample test I produced for your example:

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.easymock.EasyMock;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(A.class)
public class BTest{

    @Test
    public void thatCallingClassMakesExpectedCalls() {
        final A mockA = PowerMock.createMock(A.class);
        EasyMock.expect(mockA.doStuff()).andReturn("lol").anyTimes();

        PowerMock.mockStatic(A.class);
        EasyMock.expect(A.getInstance()).andReturn(mockA).anyTimes();

        PowerMock.replayAll(mockA);

        final B callingClass = new B();
        final String doBdo = callingClass.doBdo();
        assertThat(doBdo, is("lol"));

        EasyMock.verify(mockA);
        PowerMock.verifyAll();
    }
}
like image 71
Dan Temple Avatar answered Oct 04 '22 19:10

Dan Temple