Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking static methods with PowerMock and Mockito

Tags:

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?

Update:

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?

like image 663
Naftuli Kay Avatar asked Jan 15 '14 03:01

Naftuli Kay


People also ask

Can we use Mockito and PowerMock together?

Of course you can – and probably will – use Mockito and PowerMock in the same JUnit test at some point of time.

Can we mock static method using Mockito?

Since static method belongs to the class, there is no way in Mockito to mock static methods.

How do you mock with PowerMock?

Step 1: Create a class that contains a private method. We have created class with the name Utility and defined a private method and a public method (that returns the object of private method). Step 2: Create a JUnit test case named PowerMock_test for testing purpose.


1 Answers

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.

Full code

@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!

like image 198
MariuszS Avatar answered Sep 25 '22 11:09

MariuszS