1

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.

Pranav Kapoor
  • 1,171
  • 3
  • 13
  • 32
  • Possible duplicate of [Is it possible to use partial mocking for private static methods in PowerMock?](http://stackoverflow.com/questions/9719919/is-it-possible-to-use-partial-mocking-for-private-static-methods-in-powermock) – Turing85 Sep 27 '16 at 09:29

2 Answers2

2

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).

Spotted
  • 4,021
  • 17
  • 33
  • I get what you are saying, but the siteTranslator is built with data it fetches over the network. Since it is an expensive operation, I can only do it once in my application(and store that data inside siteTranslator), hence the static nature. I cant afford to build a new object of SiteUtil everytime I need to call getParent. – Pranav Kapoor Sep 27 '16 at 11:14
  • 1
    @PranavKapoor I perfectly got that the real `SiteTranslator` is costly to instanciate and it's perfectly fine. But if you build a new `SiteUtil` with a `DummySiteTranslator` it's not a problem at all (no `SiteTranslator` involved !). In production code you are still able to construct only one `SiteUtil` with the real `SiteTranslator` though. – Spotted Sep 27 '16 at 11:20
  • @PranavKapoor This is the answer to go with. You do not need Powermock; and when you dont need it: dont use it. It is as simply as that. – GhostCat Sep 27 '16 at 13:11
1

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.

GhostCat
  • 137,827
  • 25
  • 176
  • 248