497

I've written a factory to produce java.sql.Connection objects:

public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory {

    @Override public Connection getConnection() {
        try {
            return DriverManager.getConnection(...);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

I'd like to validate the parameters passed to DriverManager.getConnection, but I don't know how to mock a static method. I'm using JUnit 4 and Mockito for my test cases. Is there a good way to mock/verify this specific use-case?

Gerold Broser
  • 14,080
  • 5
  • 48
  • 107
Naftuli Kay
  • 87,710
  • 93
  • 269
  • 411
  • 1
    would this help? http://stackoverflow.com/questions/19464975/how-to-mock-drivermanager-getconnection – sasankad Jan 14 '14 at 03:13
  • You can't with mockito. You'd need to use powermock on top of it. – Brian Roach Jan 14 '14 at 03:27
  • 7
    You can't with mockito **by desing** :) – MariuszS Jan 14 '14 at 14:25
  • I know it's not what you asked about, but this is done easily with [JMockit](http://jmockit.github.io/). – Joe Dec 31 '14 at 09:48
  • 40
    @MariuszS It isn't by design that Mockito (or EasyMock, or jMock) does not support mocking `static` methods, but *by accident*. This limitation (along with no support for mocking `final` classes/methods, or `new`-ed objects) is a natural (but unintended) consequence of the approach employed to implement mocking, where new classes are dynamically created that implement/extend the type to be mocked; other mocking libraries use other approaches which avoid these limitations. This happened in the .NET world as well. – Rogério Nov 02 '15 at 17:28
  • 6
    @Rogério Thanks for explanation. https://github.com/mockito/mockito/wiki/FAQ **Can I mock static methods?** *No. Mockito prefers object orientation and dependency injection over static, procedural code that is hard to understand & change.* There is some *design* behind this limitation too :) – MariuszS Nov 02 '15 at 20:42
  • 21
    @MariuszS I read that as an attempt to dismiss legitimate use cases instead of admiting the tool has limitations that cannot be (easily) removed, and without providing any reasoned justification. BTW, [here is such a discussion](http://jmockit.org/about.html#productionCode) for the opposite viewpoint, with references. – Rogério Nov 03 '15 at 14:45
  • @Rogério the link is removed. can you please correct it – ihebiheb May 07 '19 at 16:50
  • Feature-tracking issue is https://github.com/mockito/mockito/issues/1013 – OrangeDog Aug 02 '19 at 13:42
  • 16
    Mockito now supports mocking static methods since v3.4.0 https://github.com/mockito/mockito/pull/1955 – Mahatma_Fatal_Error Jul 11 '20 at 11:24
  • 5
    See also [Mockito \[3.4.0\] can mock static methods!](https://asolntsev.github.io/en/2020/07/11/mockito-static-methods/). – Gerold Broser Jul 16 '20 at 00:54

22 Answers22

411

Use PowerMockito on top of Mockito.

Example code:

@RunWith(PowerMockRunner.class)
@PrepareForTest(DriverManager.class)
public class Mocker {

    @Test
    public void shouldVerifyParameters() throws Exception {

        //given
        PowerMockito.mockStatic(DriverManager.class);
        BDDMockito.given(DriverManager.getConnection(...)).willReturn(...);

        //when
        sut.execute(); // System Under Test (sut)

        //then
        PowerMockito.verifyStatic();
        DriverManager.getConnection(...);

    }

More information:

MariuszS
  • 30,646
  • 12
  • 114
  • 155
  • 6
    While this works in theory, [having a hard time in practice](https://stackoverflow.com/questions/21128690/mocking-static-methods-with-powermock-and-mockito)... – Naftuli Kay Jan 16 '14 at 00:07
  • 55
    Unfortunately the huge disadvantage of this is the need for PowerMockRunner though. – Innokenty Nov 26 '14 at 10:21
  • 18
    sut.execute() ? Means? – TejjD Dec 29 '15 at 05:22
  • 5
    System Under Test, the class that requires mock of DriverManager. http://www.kaczanowscy.pl/tomek/2011-01/testing-basics-sut-and-docs – MariuszS Dec 29 '15 at 09:36
  • why does this line executes "DriverManager.getConnection(...)" method `BDDMockito.given(DriverManager.getConnection(...)).willReturn(...);` – sattu Apr 05 '16 at 15:00
  • @sattu This is recording behaviour (stub method calls). Instruction: When someone invoke this method, then return this. – MariuszS Apr 07 '16 at 16:51
  • 1
    PowerMockito is so pretty solution but it not suitable for some cases, for example jacoco does not consider tests coverage which methods and classes have used PowerMockito – M2E67 Oct 22 '16 at 10:02
  • 1
    generally it is better to refactor and do not use static methods – MariuszS Oct 24 '16 at 09:34
  • Note that PowerMockito mocks/stubs *all* static methods of a class. So if you want to mock `ClassA::methodY` to test `ClassA::methodX`, it's not going to work well. In that case, `ClassA::methodY` has to be extracted to `ClassB`. – jakub.g Sep 27 '17 at 09:56
  • 8
    FYI, if you're already using JUnit4 you can do `@RunWith(PowerMockRunner.class)` and below that `@PowerMockRunnerDelegate(JUnit4.class)`. – EM-Creations Oct 26 '17 at 12:47
  • what about 2 static method in different classes? – Zafer Celaloglu Dec 04 '17 at 10:03
  • @ZaferCelaloglu try to specify two classes inside `@PrepareForTest` – MariuszS Dec 04 '17 at 11:02
  • 1
    I used powermock in the past and it's a good tool, but really you should use it with caution. It comes with a lot of quirks, which is no surprise considering how it works. Most times your just better off wrapping stuff in methods. – Gellweiler Mar 28 '18 at 18:51
  • And if your mocking target is used inside another class object instance, remeber to prepare the class in the @PrepareForTest annotation! :-) – eventhorizon May 24 '18 at 13:52
  • @PhilipRego `getConnection` return void? – MariuszS Apr 09 '19 at 16:33
  • 1
    @PhilipRego this answer is not about your function, it is about function with the result. Read about stubbing void methods first: https://static.javadoc.io/org.mockito/mockito-core/2.26.0/org/mockito/Mockito.html#do_family_methods_stubs – MariuszS Apr 09 '19 at 16:38
  • @PhilipRego this is true that you cannot mix ArgumentMatchers and real objects. So stick to ArgumentMatchers for all parameters. – MariuszS Apr 09 '19 at 19:26
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/191564/discussion-between-philip-rego-and-mariuszs). – Philip Rego Apr 09 '19 at 19:44
  • Well this works fine but the problem now I'm facing is, other static method of my Util class are returning null & I don't know how to get those real methods to work without mocking them. I want those methods to run as they are, but since I've enabled powermocking on this whole class PowerMockito.mockStatic(DateTimeUtility.class); I am stuck with it – NoisyBoy Apr 18 '19 at 19:20
  • What are the dependencies which should be used here for Powermock? because https://mvnrepository.com/artifact/org.powermock contains lot of different artifacts. – Imal Apr 09 '20 at 10:55
  • It's easy to by using Functional Interface class , which is nothing but a function pointer to static method but wrapped in a class, and hence possible to mock – Shrikant Prabhu Jun 19 '20 at 12:03
  • still gives me `MissingMethodInvocationException` with this ```mockStatic(Base64.class); given(Base64.getDecoder()).willReturn(mockDecoder);``` – Akshay Hazari Apr 06 '21 at 04:46
124

Mocking of static methods in Mockito is possible since Mockito 3.4.0. For more details see:

https://github.com/mockito/mockito/releases/tag/v3.4.0

https://github.com/mockito/mockito/issues/1013

https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#static_mocks

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

In your case, something like this:

  @Test
  public void testStaticMockWithVerification() throws SQLException {
    try (MockedStatic<DriverManager> dummy = Mockito.mockStatic(DriverManager.class)) {
      DatabaseConnectionFactory factory = new MySQLDatabaseConnectionFactory();
      dummy.when(() -> DriverManager.getConnection("arg1", "arg2", "arg3"))
        .thenReturn(new Connection() {/*...*/});

      factory.getConnection();

      dummy.verify(() -> DriverManager.getConnection(eq("arg1"), eq("arg2"), eq("arg3")));
    }
  }

NOTE: mocking STATIC METHODS requires mockito-inline dependency instead of mockito-core.

For JUnit5 also add this:

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-junit-jupiter</artifactId>
  <version>${mockito.version}</version>
  <scope>test</scope>
</dependency>
JohnK
  • 6,865
  • 8
  • 49
  • 75
leokom
  • 1,862
  • 1
  • 15
  • 26
  • 3
    See also [Mockito can mock static methods!](https://asolntsev.github.io/en/2020/07/11/mockito-static-methods/) and https://github.com/mockito/mockito/pull/1955. – Gerold Broser Jul 16 '20 at 00:50
  • 1
    For me one Testclass gave some really good insight, how to use the new statickMock-Feature: [StaticMockTest.java](https://github.com/mockito/mockito/blob/4767db122f95e5302ad6a60a6efaa69f9d7e6cb1/subprojects/inline/src/test/java/org/mockitoinline/StaticMockTest.java) and please see also the Bugfixes in Version [3.4.2](https://github.com/mockito/mockito/blob/release/3.x/doc/release-notes/official.md#342) and [3.4.6](https://github.com/mockito/mockito/blob/release/3.x/doc/release-notes/official.md#346) – MichaelCkr Aug 17 '20 at 11:03
  • Cannot resolve method 'mockStatic' in 'Mockito' – Harshad Panmand Jul 07 '21 at 11:01
  • @HarshadPanmand which Mockito version do you use? https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#mockStatic-java.lang.Class- – leokom Jul 07 '21 at 11:44
  • 1
    It's working now by removing 'org.mockito:mockito-all:2.0.2-beta' dependency. Previously, I used multiple dependencies of mockito like (core, inline, all). – Harshad Panmand Jul 07 '21 at 15:13
  • 1
    Thank you leokom for pointing this out. However, I am not a fan of the try-with-resources approach that Mockito came up with and thus have replaced it with an JUnit5 Extension. I'll add an answer to describe it. It allows you to simply create an annotated field on your test class for the static mock. Much cleaner, especially when you need to mock multiple statics. – Radboud Feb 17 '22 at 14:40
  • 1
    See this answer for an example with arguments: https://stackoverflow.com/a/64714397/10318564 – Yaniv K. Jun 15 '22 at 20:48
  • Use **Mockito.CALLS_REAL_METHODS** in Mockito.mockStatic in order to make other methods work as usual. i.e if you mock LocalDate.now() you want LocalDate.of() still working as usual and not returning null. `try (MockedStatic localDateStaticMock = Mockito.mockStatic(LocalDate.class, Mockito.CALLS_REAL_METHODS)) { localDateStaticMock.when(LocalDate::now).thenReturn(TODAY_IS_2023_03_13);` – R.A Mar 14 '23 at 09:26
88

The typical strategy for dodging static methods that you have no way of avoiding using, is by creating wrapped objects and using the wrapper objects instead.

The wrapper objects become facades to the real static classes, and you do not test those.

A wrapper object could be something like

public class Slf4jMdcWrapper {
    public static final Slf4jMdcWrapper SINGLETON = new Slf4jMdcWrapper();

    public String myApisToTheSaticMethodsInSlf4jMdcStaticUtilityClass() {
        return MDC.getWhateverIWant();
    }
}

Finally, your class under test can use this singleton object by, for example, having a default constructor for real life use:

public class SomeClassUnderTest {
    final Slf4jMdcWrapper myMockableObject;

    /** constructor used by CDI or whatever real life use case */
    public myClassUnderTestContructor() {
        this.myMockableObject = Slf4jMdcWrapper.SINGLETON;
    }

    /** constructor used in tests*/
    myClassUnderTestContructor(Slf4jMdcWrapper myMock) {
        this.myMockableObject = myMock;
    }
}

And here you have a class that can easily be tested, because you do not directly use a class with static methods.

If you are using CDI and can make use of the @Inject annotation then it is even easier. Just make your Wrapper bean @ApplicationScoped, get that thing injected as a collaborator (you do not even need messy constructors for testing), and go on with the mocking.

codebox
  • 19,927
  • 9
  • 63
  • 81
99Sono
  • 3,554
  • 27
  • 39
  • 3
    I created a tool to automatically generate Java 8 "mixin" interfaces which wrap static calls: https://github.com/aro-tech/interface-it The generated mixins can be mocked like any other interface, or if your class under test "implements" the interface you can override any of its methods in a subclass for the test. – aro_tech Oct 14 '16 at 21:45
31

I had a similar issue. The accepted answer did not work for me, until I made the change: @PrepareForTest(TheClassThatContainsStaticMethod.class), according to PowerMock's documentation for mockStatic.

And I don't have to use BDDMockito.

My class:

public class SmokeRouteBuilder {
    public static String smokeMessageId() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("Exception occurred while fetching localhost address", e);
            return UUID.randomUUID().toString();
        }
    }
}

My test class:

@RunWith(PowerMockRunner.class)
@PrepareForTest(SmokeRouteBuilder.class)
public class SmokeRouteBuilderTest {
    @Test
    public void testSmokeMessageId_exception() throws UnknownHostException {
        UUID id = UUID.randomUUID();

        mockStatic(InetAddress.class);
        mockStatic(UUID.class);
        when(InetAddress.getLocalHost()).thenThrow(UnknownHostException.class);
        when(UUID.randomUUID()).thenReturn(id);

        assertEquals(id.toString(), SmokeRouteBuilder.smokeMessageId());
    }
}
6324
  • 4,678
  • 8
  • 34
  • 63
26

For those who use JUnit 5, Powermock is not an option. You'll require the following dependencies to successfully mock a static method with just Mockito.

testCompile    group: 'org.mockito', name: 'mockito-core',           version: '3.6.0'
testCompile    group: 'org.mockito', name: 'mockito-junit-jupiter',  version: '3.6.0'
testCompile    group: 'org.mockito', name: 'mockito-inline',         version: '3.6.0'

mockito-junit-jupiter add supports for JUnit 5.

And support for mocking static methods is provided by mockito-inline dependency.

Example:

@Test
void returnUtilTest() {
    assertEquals("foo", UtilClass.staticMethod("foo"));

    try (MockedStatic<UtilClass> classMock = mockStatic(UtilClass.class)) {

        classMock.when(() -> UtilClass.staticMethod("foo")).thenReturn("bar");

        assertEquals("bar", UtilClass.staticMethod("foo"));
     }

     assertEquals("foo", UtilClass.staticMethod("foo"));
}

The try-with-resource block is used to make the static mock remains temporary, so it's mocked only within that scope.

When not using a try block, make sure to close the mock, once you are done with the assertions.

MockedStatic<UtilClass> classMock = mockStatic(UtilClass.class)
classMock.when(() -> UtilClass.staticMethod("foo")).thenReturn("bar");
assertEquals("bar", UtilClass.staticMethod("foo"));
classMock.close();

Mocking void methods:

When mockStatic is called on a class, all the static void methods in that class automatically get mocked to doNothing().

Gayan Weerakutti
  • 11,904
  • 2
  • 71
  • 68
25

As mentioned before you can not mock static methods with mockito.

If changing your testing framework is not an option you can do the following:

Create an interface for DriverManager, mock this interface, inject it via some kind of dependency injection and verify on that mock.

ChrisM
  • 1,148
  • 8
  • 22
8

Observation : When you call static method within a static entity, you need to change the class in @PrepareForTest.

For e.g. :

securityAlgo = MessageDigest.getInstance(SECURITY_ALGORITHM);

For the above code if you need to mock MessageDigest class, use

@PrepareForTest(MessageDigest.class)

While if you have something like below :

public class CustomObjectRule {

    object = DatatypeConverter.printHexBinary(MessageDigest.getInstance(SECURITY_ALGORITHM)
             .digest(message.getBytes(ENCODING)));

}

then, you'd need to prepare the class this code resides in.

@PrepareForTest(CustomObjectRule.class)

And then mock the method :

PowerMockito.mockStatic(MessageDigest.class);
PowerMockito.when(MessageDigest.getInstance(Mockito.anyString()))
      .thenThrow(new RuntimeException());
some random guy
  • 430
  • 5
  • 9
  • I was banging my head against the wall trying to figure out why my static class wasn't mocking. You would think in all the tutorials on the interwebs, ONE would have gone into more than the bare-bones use case. – SoftwareSavant Jul 06 '18 at 01:06
7

You can do it with a little bit of refactoring:

public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory {

    @Override public Connection getConnection() {
        try {
            return _getConnection(...some params...);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //method to forward parameters, enabling mocking, extension, etc
    Connection _getConnection(...some params...) throws SQLException {
        return DriverManager.getConnection(...some params...);
    }
}

Then you can extend your class MySQLDatabaseConnectionFactory to return a mocked connection, do assertions on the parameters, etc.

The extended class can reside within the test case, if it's located in the same package (which I encourage you to do)

public class MockedConnectionFactory extends MySQLDatabaseConnectionFactory {

    Connection _getConnection(...some params...) throws SQLException {
        if (some param != something) throw new InvalidParameterException();

        //consider mocking some methods with when(yourMock.something()).thenReturn(value)
        return Mockito.mock(Connection.class);
    }
}
Fermin Silva
  • 3,331
  • 2
  • 17
  • 21
7

I also wrote a combination of Mockito and AspectJ: https://github.com/iirekm/varia/tree/develop/ajmock

Your example becomes:

when(() -> DriverManager.getConnection(...)).thenReturn(...);
iirekm
  • 8,890
  • 5
  • 36
  • 46
6

Mockito cannot capture static methods, but since Mockito 2.14.0 you can simulate it by creating invocation instances of static methods.

Example (extracted from their tests):

public class StaticMockingExperimentTest extends TestBase {

    Foo mock = Mockito.mock(Foo.class);
    MockHandler handler = Mockito.mockingDetails(mock).getMockHandler();
    Method staticMethod;
    InvocationFactory.RealMethodBehavior realMethod = new InvocationFactory.RealMethodBehavior() {
        @Override
        public Object call() throws Throwable {
            return null;
        }
    };

    @Before
    public void before() throws Throwable {
        staticMethod = Foo.class.getDeclaredMethod("staticMethod", String.class);
    }

    @Test
    public void verify_static_method() throws Throwable {
        //register staticMethod call on mock
        Invocation invocation = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "some arg");
        handler.handle(invocation);

        //verify staticMethod on mock
        //Mockito cannot capture static methods so we will simulate this scenario in 3 steps:
        //1. Call standard 'verify' method. Internally, it will add verificationMode to the thread local state.
        //  Effectively, we indicate to Mockito that right now we are about to verify a method call on this mock.
        verify(mock);
        //2. Create the invocation instance using the new public API
        //  Mockito cannot capture static methods but we can create an invocation instance of that static invocation
        Invocation verification = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "some arg");
        //3. Make Mockito handle the static method invocation
        //  Mockito will find verification mode in thread local state and will try verify the invocation
        handler.handle(verification);

        //verify zero times, method with different argument
        verify(mock, times(0));
        Invocation differentArg = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "different arg");
        handler.handle(differentArg);
    }

    @Test
    public void stubbing_static_method() throws Throwable {
        //register staticMethod call on mock
        Invocation invocation = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "foo");
        handler.handle(invocation);

        //register stubbing
        when(null).thenReturn("hey");

        //validate stubbed return value
        assertEquals("hey", handler.handle(invocation));
        assertEquals("hey", handler.handle(invocation));

        //default null value is returned if invoked with different argument
        Invocation differentArg = Mockito.framework().getInvocationFactory().createInvocation(mock, withSettings().build(Foo.class), staticMethod, realMethod,
                "different arg");
        assertEquals(null, handler.handle(differentArg));
    }

    static class Foo {

        private final String arg;

        public Foo(String arg) {
            this.arg = arg;
        }

        public static String staticMethod(String arg) {
            return "";
        }

        @Override
        public String toString() {
            return "foo:" + arg;
        }
    }
}

Their goal is not to directly support static mocking, but to improve its public APIs so that other libraries, like Powermockito, don't have to rely on internal APIs or directly have to duplicate some Mockito code. (source)

Disclaimer: Mockito team thinks that the road to hell is paved with static methods. However, Mockito's job is not to protect your code from static methods. If you don’t like your team doing static mocking, stop using Powermockito in your organization. Mockito needs to evolve as a toolkit with an opinionated vision on how Java tests should be written (e.g. don't mock statics!!!). However, Mockito is not dogmatic. We don't want to block unrecommended use cases like static mocking. It's just not our job.

David Miguel
  • 12,154
  • 3
  • 66
  • 68
6

I found one solution in Mockito. This feature comes with a version only from 3.4.0

https://asolntsev.github.io/en/2020/07/11/mockito-static-methods/

  • dependency

    In your build.gradle replace mockito-core:3.3.3 by mockito-inline:3.4.0:

    testImplementation('org.mockito:mockito-inline:3.4.0')
    
  • what are we going to mock

     class Buddy 
     {
         static String name() 
         {
            return "John";
         }
     }
    
  • Mock the static method

        @Test
        void lookMomICanMockStaticMethods() 
        {
             assertThat(Buddy.name()).isEqualTo("John");
    
            try (MockedStatic<Buddy> theMock = Mockito.mockStatic(Buddy.class)) 
            {
                theMock.when(Buddy::name).thenReturn("Rafael");
                assertThat(Buddy.name()).isEqualTo("Rafael");
            }
    
            assertThat(Buddy.name()).isEqualTo("John");
        }
    

I think this could help us.

Dupinder Singh
  • 7,175
  • 6
  • 37
  • 61
5

To mock static method you should use a Powermock look at: https://github.com/powermock/powermock/wiki/MockStatic. Mockito doesn't provide this functionality.

You can read nice a article about mockito: http://refcardz.dzone.com/refcardz/mockito

Line
  • 1,529
  • 3
  • 18
  • 42
marek.kapowicki
  • 674
  • 2
  • 5
  • 17
  • 3
    Please don't link to a website. Answers should include actual usable answers. If the site goes down or changes, the answer is no longer valid. – the_new_mr Jul 23 '18 at 11:01
3

When you try to mock the static method, you have to write the test inside the try block. because It's important to note that scoped mocks must be closed by the entity that activates the mock.

      try (MockedStatic<Tester> tester = Mockito.mockStatic(Tester.class)) {
            tester.when(() -> Tester.testStatic("Testing..")).thenReturn(mock(ReturnObject.class));
    //Here you have to write the test cases
      }

In the above example, we have to mock the Tester Class testStatic Method with input param as "Testing...". Here, this method will return a ReturnObject class type object. Hence we write mockito when chain like above.

Don't forget to add below dependency in your Gradle/maven

    testImplementation 'org.mockito:mockito-inline:4.3.1'
Sivaram Rasathurai
  • 5,533
  • 3
  • 22
  • 45
2

Since that method is static, it already has everything you need to use it, so it defeats the purpose of mocking. Mocking the static methods is considered to be a bad practice.

If you try to do that, it means there is something wrong with the way you want to perform testing.

Of course you can use PowerMockito or any other framework capable of doing that, but try to rethink your approach.

For example: try to mock/provide the objects, which that static method consumes instead.

Benas
  • 2,106
  • 2
  • 39
  • 66
1

Here I share my mockito MockStatic solution based on an extension as promised in my answer to leokom's solution.

So, why does Mockito choose try-with-resources? Well, simply because they want to keep a tidy ship. That is good programming after all. Try-with-resources allows construction with guaranteed calling of the close method. But in JUnit we already have that in BeforeEach and AfterEach. And one can easily add these for a generic purpose to each test class using an Extension that implements BeforeEachCallback and AfterEachCallback.

So much for the theory. Let's make a static mock for

Instant.now()

I started with an annotation to be able to mark the fields in my test class that I want to use as static mocks.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface StaticMock {

}

This allows me to create a field in my test class for static mocking that I can easily find in my Extension class.

  @StaticMock
  private MockedStatic<Instant> staticInstantMock;

I added the Extension I created to my test class. You have two options.

  1. Create an Extension for this purpose and add it to the class next to MockitoExtension, which you also need.
  2. Create an Extension and have it inherit from MockitoExtension. Now you can replace MockitoExtension on your test class.

I used the latter of the two.

@ExtendWith({CompanyMockitoExtension.class})
class MyExtendedTestClass {

Now we need something to be returned for the static when it's called:

  @Mock
  private Instant now;

  staticInstantMock.when(Instant::now).thenReturn(now);

The whole test class:

@ExtendWith({CompanyMockitoExtension.class})
class MyExtendedTestClass {

  @StaticMock
  private MockedStatic<Instant> staticInstantMock;

  @Mock
  private Instant now;

  @Test
  void myTestMethod() {
    staticInstantMock.when(Instant::now).thenReturn(now);

    assertThat(Instant::now).isSameAs(now); // This would normally happen in the class you are testing...
  }
}

Now let's take a look at the Extension class.

import static org.mockito.Mockito.mockStatic;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

public class CompanyMockitoExtension extends MockitoExtension {

  @Override
  public void beforeEach(ExtensionContext context) {
    super.beforeEach(context); // Don't forget to call the super!!
    if (context.getTestInstance().isEmpty()) { // Just to be sure...
      return;
    }
    // Get the unit test instance
    Object testSubject = context.getTestInstance().get();
    initializeStaticMocks(testSubject);
  }

  private void initializeStaticMocks(Object testSubject) {
    // Find all fields that I want to static mock
    List<Field> staticMockFields = ReflectionHelper.getFieldsWithAnnotation(testSubject, StaticMock.class);
    staticMockFields.forEach(field -> initializeStaticMock(field, testSubject));
  }

  private void initializeStaticMock(Field field, Object testSubject) {
    // Get the type of the static mock. It is within the generic MockedStatic<> class type.
    Class<?> typeForStaticMock = (Class<?>) ReflectionHelper.getTypesForGeneric(field)[0];
    try {
      // Now set the field with the mockStatic method of Mockito.
      field.setAccessible(true);
      field.set(testSubject, mockStatic(typeForStaticMock));
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Failed to instantiate Static Mock with type: " + typeForStaticMock.getName());
    }
  }

  @Override
  public void afterEach(ExtensionContext context) {
    super.afterEach(context); // Again, do not forget to call the super.
    if (context.getTestInstance().isEmpty()) {
      return;
    }
    Object testSubject = context.getTestInstance().get();
    closeStaticMocks(testSubject); // Close all static mocks.
  }

  private void closeStaticMocks(Object testSubject) {
    // Again find all fields we annotated
    List<Field> staticMockFields = ReflectionHelper.getFieldsWithAnnotation(testSubject, StaticMock.class);
    staticMockFields.forEach(field -> closeStaticMock(field, testSubject));
  }

  private void closeStaticMock(Field field, Object testSubject) {
    // Get the instance and simply call close.
    MockedStatic<?> mockedStaticInstance = ReflectionHelper.getFieldInstance(field, testSubject, MockedStatic.class);
    mockedStaticInstance.close();
  }
}

The nice thing about this extension is that you can add additional mocking stuff. I added verification of no more interactions on all mocks in the AfterEach. This is now automatic when we use this extension. I also added similar behavior for construction mocking as for the static mocking.

As you see, I made my own reflection helper class. I know there are some standard reflection helper classes and those might be better. Here is mine for this purpose.

public class ReflectionHelper {

  public static List<Field> getFieldsWithAnnotation(
      Object testSubject,
      Class<? extends Annotation> annotationType
  ) {
    Class<?> testSubjectClass = testSubject.getClass();

    return Arrays.stream(testSubjectClass.getDeclaredFields())
                 .filter(field -> field.isAnnotationPresent(annotationType))
                 .collect(toUnmodifiableList());
  }

  public static List<Field> getCollectionFields(Object testSubject) {
    Class<?> testSubjectClass = testSubject.getClass();

    return Arrays.stream(testSubjectClass.getDeclaredFields())
                 .filter(field -> Collection.class.isAssignableFrom(field.getType()))
                 .collect(toUnmodifiableList());
  }

  @SuppressWarnings("unchecked")
  public static <T> T getFieldInstance(Field field, Object testSubject, Class<T> type) {
    return (T) getFieldInstance(field, testSubject);
  }

  public static Object getFieldInstance(Field field, Object testSubject) {
    try {
      boolean isStatic = isStatic(field.getModifiers());
      Object context = isStatic ? null : testSubject;
      field.setAccessible(true);
      return field.get(context);
    } catch (IllegalAccessException e) {
      throw new RuntimeException("Failed to get instance of field.");
    }
  }

  public static Type[] getTypesForGeneric(Field field) {
    ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
    return parameterizedType.getActualTypeArguments();
  }
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Radboud
  • 81
  • 5
1

You can mock static without adding any dependency or using PowerMock. It's by doing this trick :)

Add the file org.mockito.plugins.MockMaker under the folder src/test/resources/mockito-extensions/ with this content:

mock-maker-inline

enter image description here

jumping_monkey
  • 5,941
  • 2
  • 43
  • 58
0

Use JMockit framework. It worked for me. You don't have to write statements for mocking DBConenction.getConnection() method. Just the below code is enough.

@Mock below is mockit.Mock package

Connection jdbcConnection = Mockito.mock(Connection.class);

MockUp<DBConnection> mockUp = new MockUp<DBConnection>() {

            DBConnection singleton = new DBConnection();

            @Mock
            public DBConnection getInstance() { 
                return singleton;
            }

            @Mock
            public Connection getConnection() {
                return jdbcConnection;
            }
         };
Zlatan
  • 1
  • 1
0

There is an easy solution by using java FunctionalInterface and then add that interface as dependency for the class you are trying to unit test.

Shrikant Prabhu
  • 709
  • 8
  • 13
0

For mocking static functions i was able to do it that way:

  • create a wrapper function in some helper class/object. (using a name variant might be beneficial for keeping things separated and maintainable.)
  • use this wrapper in your codes. (Yes, codes need to be realized with testing in mind.)
  • mock the wrapper function.

wrapper code snippet (not really functional, just for illustration)

class myWrapperClass ...
    def myWrapperFunction (...) {
        return theOriginalFunction (...)
    }

of course having multiple such functions accumulated in a single wrapper class might be beneficial in terms of code reuse.

Alexander Stohr
  • 159
  • 1
  • 18
0

Refactor it a little bit:

public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory {
    ConnectionSupplier connectionSupplier = () -> SupplierDriverManager.getConnection();

    public void setConnSupplier(ConnectionSupplier supplier) {
        this.connectionSupplier = supplier;
    }

    @Override 
    public Connection getConnection() {
        try {
            return connectionSupplier.conn();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
    @FunctionalInterface
    interface ConnectionSupplier {
        Connection conn();
    }
}

Then you can use the mockito:

MySQLDatabaseConnectionFactory.ConnectionSupplier connectionSupplier = mock(MySQLDatabaseConnectionFactory.ConnectionSupplier.class);
when(connectionSupplier.conn()).thenReturn(yourMockObject);
yourConnectionFactory.setConnSupplier(connectionSupplier);
Eugene Maysyuk
  • 2,977
  • 25
  • 24
Steven Chou
  • 1,504
  • 2
  • 21
  • 43
0

Mocking LocalDate.now() for stable tests.

Please refer to @leokom answer above for more details on setting up static mock and dependencies.

Mockito.CALLS_REAL_METHODS makes sure other methods still work as usual. Otherwise LocalDate.of() would return null.

private static final LocalDate TODAY_IS_2023_03_13 = LocalDate.of(2023, 03, 13);
@Test
void now() {
try (MockedStatic<LocalDate> localDateStaticMock = Mockito.mockStatic(LocalDate.class, Mockito.CALLS_REAL_METHODS)) {
  localDateStaticMock.when(LocalDate::now).thenReturn(TODAY_IS_2023_03_13);

  assertThat(LocalDate.now()).isEqualTo(TODAY_IS_2023_03_13);
assertThat(LocalDate.of(2023,03,16)).isNotNull().isEqualTo(TODAY_IS_2023_03_13);
}

}

R.A
  • 1,813
  • 21
  • 29
0

My example how I mock with Mockito final static fields:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.ConfigConstants;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.models.*;
import org.keycloak.provider.ProviderConfigProperty;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import static org.junit.Assert.assertEquals;
import static org.junit.jupiter.api.Assertions.*;
import static org.keycloak.broker.provider.IdentityProviderMapper.ANY_PROVIDER;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.when;

@ExtendWith(MockitoExtension.class)
class HardcodedGroupMapperTest {
    @Mock
    private KeycloakSession session;
    @Mock
    private RealmModel realm;
    @Mock
    private Logger logger;
    
    private final HardcodedGroupMapper sut = new HardcodedGroupMapper();

    
     @Test
void shouldReturnEmptyTokenWhenHttpResponseStatusCode400() throws IOException {
    prepareMockEnvironment();
    when(statusLine.getStatusCode()).thenReturn(400);
    Whitebox.setInternalState(AeAuthTokenOidcMapper.class, "logger", logger);

    AccessToken token = new AccessToken();
    token.setOtherClaims("tenant", "tenant1");

    AccessToken transformedToken = sut.transformAccessToken(token, mappingModel, session, userSession, clientSessionCtx);

    String expectedJsonBody = "{\"platSite\":[\"plat-site\",\"site1\",\"site2\"],\"expiresIn\":259200,\"customerAssociation\":{\"ecrId\":\"tenant1\",\"ipAddress\":\"127.0.0.1\"}}";
    Assertions.assertNull(transformedToken.getOtherClaims().get("auth_token"));
    verify(logger).error("Bad requeset from access key API " + expectedJsonBody);
}
}

Dependency for code above (included PowerMock for mocking static final fields with Whitebox):

<dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito2</artifactId>
        <version>2.0.9</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>2.0.9</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.8.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.8.0</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-junit-jupiter</artifactId>
        <version>5.4.0</version>
        <scope>test</scope>
    </dependency>

How I mock with PowerMock static methods:

    import org.junit.Test;
import org.junit.runner.RunWith;
import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.ConfigConstants;
import org.keycloak.models.*;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.util.Map;

import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;

@RunWith(PowerMockRunner.class)
@PrepareForTest({KeycloakModelUtils.class})
class HardcodedGroupMapperStaticTest {
    public HardcodedGroupMapperStaticTest() {
    }

    @Mock
    private KeycloakSession session;
    @Mock
    private RealmModel realm;
    @Mock
    private UserModel user;
    @Mock
    private IdentityProviderMapperModel mapperModel;
    @Mock
    private BrokeredIdentityContext context;
    @Mock
    private GroupModel group;
    private final HardcodedGroupMapper sut = new HardcodedGroupMapper();

    @Test
    public void shouldImportNewUser() {
        String testGroup = "testGroup";
        when(mapperModel.getConfig()).thenReturn(Map.of(ConfigConstants.GROUP, testGroup));
        mockStatic(KeycloakModelUtils.class);
        when(KeycloakModelUtils.findGroupByPath(realm, testGroup)).thenReturn(group);
        sut.importNewUser(session, realm, user, mapperModel, context);
        verify(user).joinGroup(group);
// assert ...
// assert ...
    }

}

Dependency for code above (https://howtodoinjava.com/java/library/mock-testing-using-powermock-with-junit-and-mockito/):

    <dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.12.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-core</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

Watch carrefully your dependencies and not mix Mockito @Mock and Powermock @Mock annotations.

Yaroslav
  • 486
  • 1
  • 4
  • 14