I\'ve written a factory to produce java.sql.Connection
objects:
public class MySQLDatabaseConnectionFactory implements DatabaseConnectionFactory
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
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.
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-catch block is used to make the static mock remains temporary, so it's mocked only within that scope. But It's not mandatory to use a try-catch block.
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.
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);
}
}
Mocking of static methods in Mockito is possible since Mockito 3.4.0. For more details see:
https://github.com/mockito/mockito/tree/v3.4.0
https://github.com/mockito/mockito/issues/1013.
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: this feature requires mockito-inline dependency.