Pranav Kapoor
Pranav Kapoor

Reputation: 1281

Using power mockito to suppress private static method

I have made a small example of my code to illustrate the problem

public class SiteTranslator {
   Integer id;
   //Other fields
}

public class SiteUtil {

  private static SiteTranslator siteTranslator = getSiteTranslator();

  private static SiteTranslator getSiteTranslator()
  {
    SiteTranslator siteTranslator;
    //Logic involving network call
    return siteTranslator;
  }

  private static String getEnvironment()
  {
    String env = "";
    //Logic
    return env;
  }

  public static int getParent(int siteId)
  {
    int parentId = 0;
    //Logic using siteTranslator from getSiteTranslator()
    return parentId;
  }
}


public class SiteUtilTest {
  @Test
  public void test1()
  {
    try
    {
        PowerMockito.suppress(SiteUtil.class.getMethod("getSiteTranslator")); 
        BDDMockito.given(SiteUtil.getParent(1)).willReturn(6);
    }
    catch(Exception e)
    {
        e.printStackTrace();
    }
  }
}

The SiteTranslator object we get from getSiteTranslator() method is used by my public function getParent(). Since getSiteTranslator() requires a network call , it needs to be suppressed. I however get the following error

java.lang.NoSuchMethodException: SiteUtil.getSiteTranslator()

I believe the problem is because I'm trying to mock a private static function. However I cannot change it to public. Is there a way to mock the code in its current state.

Upvotes: 1

Views: 1041

Answers (2)

GhostCat
GhostCat

Reputation: 140525

The answer by "Spotted" already nails it, as the core problem is: you created hard-to-test code for absolutely no reason.

Using such internal static calls simply makes your program hard to test; and surprise: it also makes it hard to maintain, enhance, reuse. The fact that you need to turn to Powermock is very often simply an indication that your production code is bad. Now you can choose between using PowerMock to "fix" that problem; or to really fix the problem, by changing your production code - it is simply bad practice to solve problems the way your example code does!

So, the other real lesson here is: you want to spend some time to learn how to write code that does not have such problems; for example by watching those videos.

Upvotes: 1

Spotted
Spotted

Reputation: 4091

In fact, you don't need Powermockito to achieve what you need.

At the moment, you think you need Powermockito to suppress a private static method but this is definitely not the way to go.

Instead you should refactor your code to make it easier to test:

  • Remove static qualifiers
  • Use dependency injection

After such a refactor, you end up with something like that (no mocking needed !):

public class SiteUtil {
    private SiteTranslator siteTranslator;

    public SiteUtil(SiteTranslator siteTranslator) {
        this.siteTranslator = siteTranslator;
    }

    public int getParent(int siteId) {
        int parentId = 0;
        // Logic using siteTranslator
        return parentId;
    }

    ...
}

Now you can test it like that:

public class SiteUtilSpec {
    private final SiteTranslator defaultTranslator = new DummySiteTranslator();

    @Test
    public void itShouldReturnTheSixthSiteWhenWeProvideTheFirstParent() {
        SiteUtil site = new SiteUtil(defaultTranslator);

        int parentId = site.getParent(1);

        assertEquals(6, parentId);
    }
}

DummySiteTranslator is a fake object (maybe it is embedding a bunch of hardcoded translations useful for testing) but the point is that this object never do any network call ! Making its usage safe and fast (ideal for testing).

Upvotes: 2

Related Questions