web-dev-qa-db-ja.com

PowerMockとMockitoによる静的メソッドのモック

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内のクラスへの参照をモックしていないようです。どうすればこれを回避できますか?

23
Naftuli Kay

@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 のおかげで、この問題を解決できました!

30
MariuszS