I'm trying to verify a call to java.sql.DriverManager.getConnection
using JUnit, Mockito, and PowerMock.
Here's my test case:
@RunWith(PowerMockRunner.class)
@PrepareForTest(DriverManager.class)
public class MySQLDatabaseConnectionFactoryTest {
private ConfigurationService configurationService;
private MySQLDatabaseConnectionFactory reference;
@Before
public void setUp() throws Exception {
this.reference = new MySQLDatabaseConnectionFactory();
}
@Test
public void testGetConnection() throws SQLException {
// setup
Connection connection = mock(Connection.class);
PowerMockito.mockStatic(DriverManager.class);
when(DriverManager.getConnection(anyString(), anyString(), anyString())).thenReturn(connection);
// run
this.reference.getConnection();
// verify
PowerMockito.verifyStatic();
DriverManager.getConnection("jdbc:mysql://myhost:1111/database", "username", "password");
}
}
Here's the code under test:
public class MySQLDatabaseConnectionFactory implements
DatabaseConnectionFactory {
@Override
public Connection getConnection(IApplicationInstance appInstance) {
try {
return DriverManager.getConnection(String.format("jdbc:mysql://%s:%d/%s",
MYSQL_HOST, MYSQL_PORT, MYSQL_DATABASE), MYSQL_USERNAME, MYSQL_PASSWORD);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
Interestingly enough, this code fails with a java.sql.SQLException
:
java.lang.RuntimeException: java.sql.SQLException: No suitable driver found for jdbc:mysql://myhost:1111/database
Now, I could easily just make sure that my SQL driver (MySQL in this case) is loaded at test time, but why isn't the static method completely mocked out without side-effects?
I've better isolated the problem. I've added a test method to my test case which tries getting a connection from DriverManager
:
@Test
public void testSomething() {
Connection conn = mock(Connection.class);
mockStatic(DriverManager.class);
when(DriverManager.getConnection(anyString())).thenReturn(conn);
Connection c = DriverManager.getConnection("whut");
verifyStatic();
DriverManager.getConnection("whut");
}
This test actually passes, while the other test still fails. It seems that PowerMock isn't mocking the reference to the class inside of MySQLDatabaseConnectionFactory
. How can I work around this?
Changing your @PrepareForTest
annotation value to MySQLDatabaseConnectionFactory.class
will resolve this issue.
This annotation tells PowerMock to prepare certain classes for testing. Classes needed to be defined using this annotation are typically those that needs to be byte-code manipulated. This includes final classes, classes with final, private, static.
In this situation PowerMockito have to replace invocation to static method DriverManager.getConnection
with mocked code. This is done with usage of byte-code manipulation.
@RunWith(PowerMockRunner.class)
@PrepareForTest(MySQLDatabaseConnectionFactory.class)
public class MySQLDatabaseConnectionFactoryTest {
private MySQLDatabaseConnectionFactory reference;
@Before
public void setUp() throws Exception {
reference = new MySQLDatabaseConnectionFactory();
}
@Test
public void testGetConnection() throws SQLException {
// given
PowerMockito.mockStatic(DriverManager.class);
BDDMockito.given(DriverManager.getConnection(anyString(), anyString(), anyString()))
.willReturn(mock(Connection.class));
// when
reference.getConnection();
// then
PowerMockito.verifyStatic();
DriverManager.getConnection("jdbc:mysql://myhost:1111/database", "username", "password");
}
}
Thanks to @Szpak for helping me to resolve this issue!