JUnit、Mockito、PowerMockを使用してJava.sql.DriverManager.getConnection
の呼び出しを確認しようとしています。
これが私のテストケースです:
@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");
}
}
テスト中のコードは次のとおりです。
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);
}
}
}
興味深いことに、このコードはJava.sql.SQLException
で失敗します。
Java.lang.RuntimeException: Java.sql.SQLException: No suitable driver found for jdbc:mysql://myhost:1111/database
これで、テスト時にSQLドライバー(この場合はMySQL)が読み込まれることを簡単に確認できましたが、静的メソッドが副作用なしに完全にモックアウトされないのはなぜですか?
私は問題をよりよく切り分けました。 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");
}
このテストは実際にはパスしますが、他のテストはまだ失敗します。 PowerMockがMySQLDatabaseConnectionFactory
内のクラスへの参照をモックしていないようです。どうすればこれを回避できますか?
@PrepareForTest
アノテーション値をMySQLDatabaseConnectionFactory.class
に変更すると、この問題が解決します。
この注釈は、PowerMockに特定のクラスをテスト用に準備するように指示します。このアノテーションを使用して定義する必要があるクラスは、通常、バイトコード操作が必要なクラスです。これには、最終クラス、最終クラス、プライベートクラス、静的クラスが含まれます。
この状況では、PowerMockitoは静的メソッドDriverManager.getConnection
の呼び出しをモックコードに置き換える必要があります。これは、バイトコード操作を使用して行われます。
@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");
}
}
@ Szpak のおかげで、この問題を解決できました!