简体   繁体   中英

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.

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

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM