Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stub private methods of class under test by Mockito

Tags:

java

mockito

Say we have java class called SomeClass

public class SomeClass {

    private boolean isMethod() {

        return false;
    }

    public void sendRequest(String json, String text) {

        int messageId;

        if (isMethod()) {
            messageId = getMessageId(json);
            sendMessage(messageId, text);
        } else {
            throw new IllegalArgumentException();
        }
    }

    private void sendMessage(int messageId, String text) {

    }

    private int getMessageId(String text) {

        Pattern p = Pattern.compile("messageId=(\\d+)&");
        Matcher m = p.matcher(text);

        if (m.find()) {
            return Integer.valueOf(m.group(1));
        }
        return 0;
    }
}

Don't pay attention to methods' name, they're all optional.

  • I want to test sendRequest(String json, String text) method in isolation.
  • I want to stub methods isMethod() and getMessageId(json), and verify that sendMessage(messageId, text) method is called.
  • I need to be sure that getMessageId(json) returns 25 and that isMethod() returns true no matter which argument value given.
like image 208
Vanguard Avatar asked Aug 28 '16 09:08

Vanguard


2 Answers

This could be achieved by PowerMockito framework.

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest(SomeClass.class)
public class SomeClassTest {

    private SomeClass someInstance;

    @Before
    public void setUp() throws Exception {

        someInstance = PowerMockito.spy(new SomeClass());
    }

    @Test
    public void sendRequest() throws Exception {

        String json = "JSON";
        String text = "Some text";
        int messageId = 1;

        PowerMockito.doReturn(true).when(someInstance, "isMethod");
        PowerMockito.doReturn(messageId).when(someInstance, "getMessageId", json);

        someInstance.sendRequest(json, text);

        PowerMockito.verifyPrivate(someInstance).invoke("isMethod");
        PowerMockito.verifyPrivate(someInstance).invoke("getMessageId", json);
        PowerMockito.verifyPrivate(someInstance).invoke("sendMessage", messageId, text);
    }

}
like image 74
Vanguard Avatar answered Sep 23 '22 10:09

Vanguard


In addition to what was said in this answer, I would add that the desire to test whether private method was called indicates that you are testing implementation as oppose to the public contract of a class. It is not an optimal idea, because it makes your refactoring more difficult. The idea of refactoring is that you can change inner workings of your code without violating it's contract. And your unit tests help you make sure that while changing the implementation, you did not introduce any changes to the contract.

So, in your case a better option would be to restructure your code so that your test calls public methods and verifies the result based on return values

like image 40
oldbam Avatar answered Sep 20 '22 10:09

oldbam