How to mock static method without powermock

102,030

Solution 1

(I assume you can use Mockito though) Nothing dedicated comes to my mind but I tend to use the following strategy when it comes to situations like that:

1) In the class under test, replace the static direct call with a call to a package level method that wraps the static call itself:

public class ToBeTested{

    public void myMethodToTest(){
         ...
         String s = makeStaticWrappedCall();
         ...
    }

    String makeStaticWrappedCall(){
        return Util.staticMethodCall();
    }
}

2) Spy the class under test while testing and mock the wrapped package level method:

public class ToBeTestedTest{

    @Spy
    ToBeTested tbTestedSpy = new ToBeTested();

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void myMethodToTestTest() throws Exception{
       // Arrange
       doReturn("Expected String").when(tbTestedSpy).makeStaticWrappedCall();

       // Act
       tbTestedSpy.myMethodToTest();
    }
}

Here is an article I wrote on spying that includes similar case, if you need more insight: sourceartists.com/mockito-spying

Solution 2

When you have static code that gives you trouble in your unit tests; so that you feel you have to "mock it away", you have exactly these options:

  • You turn to PowerMock(ito). Works fine.
  • You turn to JMockit. Works fine, too.
  • If you are testing code you have written yourself, you might want to step back and ask yourself: "why did I write code that I now find hard to unit test?"

In other words: if you want to use a mocking framework, you have to use one of those listed above. On the one side, that is absolutely fair. static is one part of the Java language; so why not use a framework that allows you to deal with it?

But of course: you still have the static call in your production code then. Leading to tight coupling, and preventing polymorphism.

So: if you can get rid of the static call (even when just using the workaround suggested in the other answer) - all the better. If not: Mockito can't help; you need the magic of byte code manipulation resp. JVM agents.

Solution 3

You can use Mockito (since version 3.4.0) to mock static methods.

Given a class Foo:

class Foo{
  static String method() {
    return "foo";
  }
}

This is the test:

@Test
void testMethod() {
    assertEquals("foo", Foo.method());
    try (MockedStatic mocked = Mockito.mockStatic(Foo.class)) {
        mocked.when(Foo::method).thenReturn("bar");
        assertEquals("bar", Foo.method());
        mocked.verify(Foo::method);
    }
    assertEquals("foo", Foo.method());
}

This requires the dependency org.mockito:mockito-inline:3.4.0 or newer version.

Solution 4

I've had a lot of luck with doing something similar to what Maciej suggested in his answer above. In Java8 I like to wrap those static methods with functional interfaces to make them more straightforward to inject or mock. For example:

public class MyClass {
    private MyStaticWrapper staticWrapper;

    public MyClass(final MyStaticWrapper staticWrapper) {
        this.staticWrapper = staticWrapper;
    }

    public void main() {
        ...
        staticWrapper.doSomething();
        ...    
    }
}    

public interface MyStaticWrapper {
    default void doSomething() {
      Util.annoyingUntestableStaticFunction();
    }
}
Share:
102,030

Related videos on Youtube

gati sahu
Author by

gati sahu

Updated on July 25, 2022

Comments

  • gati sahu
    gati sahu almost 2 years

    Is there any way we can mock the static util method while testing in JUnit?

    I know Powermock can mock static calls, but I don't want to use Powermock.

    Are there any alternatives?

    • Abubakkar
      Abubakkar almost 7 years
      You cannot mock static methods even if you use Mockito.
    • Uwe Allner
      Uwe Allner almost 7 years
      Is there a reason why you do not want to use powermock?
    • Arek
      Arek almost 7 years
      Do you have to mock it? Is it your code and you can rewrite it so that there will be no statics? Static methods are death to testability
    • gati sahu
      gati sahu almost 7 years
      Those are util class use by other module so I can not change
    • GhostCat
      GhostCat almost 6 years
      I noticed that your question is still "open" - as you didn't accept an answer. Please have a look and decide if you want to accept an answer. Or let me know if there is something I can do to enhance my input to make it accept worthy. Accepting helps future readers to determine if the problem is solved, and shows appreciation for the people who took the time answering you. Thanks!
    • Dexter
      Dexter about 4 years
      @UweAllner, one reason for not using powermock is Code Coverage tool like JaCoCo may not consider code covered through powermock in code coverage report.
  • Talha
    Talha about 6 years
    How can you call constructor when the question clearly ask about static methods? Wouldn't it be useless to call constructor when all methods are static? Also spying will also be useless in this case.
  • thomas77
    thomas77 almost 6 years
    If the class is not final he could subclass ToBeTested adding the wrapper method and test the subclass.
  • 6rchid
    6rchid over 5 years
    I get an error Wanted but not invoked: Actually, there were zero interactions with this mock. when I try to verify whether the method was called.
  • user2000189
    user2000189 almost 4 years
    I used org.mockito:mockito-inline:3.4.0 and newer version and copied the same code as above. But still i get compilation error for "MockedStatic" and "mockStatic". it does not show any import statements. I tried using import org.mockito.*; but it does not work
  • user2000189
    user2000189 almost 4 years
    After using the edited answer as well, i am getting compilation error as "MockedStatic cannot be resolved to a type". Do we need any special import statements. My pom.xml dependecy added as <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>3.5.9</version> </dependency>
  • Marc
    Marc almost 4 years
    @user2000189 are you using SpringBoot? The latest version of spring-boot ( 2.3.3.RELEASE) does not support Mockito 3.4.0. If you want to use it, you need to force org.mockito:mockito-core to version 3.4.0.
  • user2000189
    user2000189 almost 4 years
    i am using spring-boot (2.3.1-RELEASE) and mockito-core as 3.3.3.
  • Marc
    Marc almost 4 years
    @user2000189 forcing mockito-core to version 3.4.0 will let you run this example, but be aware of other potential issues since it's not yet officially supported by Spring.
  • Mousumi Kar
    Mousumi Kar over 3 years
    hello , do you have a example of unit testing for this please ?
  • VishnuVS
    VishnuVS about 3 years
    is there any way we can mock multiple classes in a single try block @Marc
  • David Hladky
    David Hladky almost 3 years
    The try with block is used so that the object is initialized and cleaned up.So you sure can do something like try (MockedStatic<Foo1> mocked1 = Mockito.mockStatic(Foo1.class); MockedStatic<Foo2> mocked2 = Mockito.mockStatic(Foo2.class) ) { ...... }