2013-10-27 172 views
2

我正在嘗試編寫一個名爲DriverManager.getConnection()的方法的單元測試。我正在使用PowerMock 1.5和easymock。不知何故,Powermock無法模擬DriverManager並最終調用真正的DriverManager。 這是我的代碼看起來像: - 如下圖所示Java嘲笑DriverManager

@Test 
public void checkConnection() { 
    try { 
     String url = "jdbc:oracle:thin:@//myhost:1521/orcl"; 
     String password = "@55"; 
     String dbName = "Halloween"; 
     String username = "Dracula"; 

     PowerMock.mockStatic(DriverManager.class); 

     Connection connection = EasyMock.createMock(Connection.class); 
     List<Datasource> allDs = new ArrayList<Datasource>(2); 
     Datasource d1 = new Datasource(); 
     d1.setUrl(url); 
     allDs.add(d1); 
     allDs.add(new Datasource()); 

     EasyMock.expect(hibernateTemplate.find("from Datasource ds where ds.dsName = ?", dbName)).andReturn(allDs); 

     EasyMock.expect(DriverManager.getConnection(d1.getUrl(), username, password)).andReturn(connection); 

     PowerMock.replay(DriverManager.class); 
     EasyMock.replay(hibernateTemplate); 

     Connection con = testee.authenticateUserForDatabase(dbName, username, password); 
     Assert.assertNotNull(con); 

     PowerMock.verify(DriverManager.class); 
     EasyMock.verify(hibernateTemplate); 
    } catch (Exception e) { 
     Assert.fail(e.getMessage()); 
    } 
} 

我的類代碼: -

@Override 
public Connection getConnection(String dbName, String username, String password) { 
    Connection connection = null; 
    @SuppressWarnings("unchecked") 
    List<Datasource> ds = hibernateTemplate.find("from Datasource ds where ds.dsName = ?", dbName); 
    if (ds == null || ds.isEmpty()) { 
     throw new ProviderException("Invalid datasource name [" + dbName + "]"); 
    } 
    Datasource d = (Datasource) ds.get(0); 
    int retryCount = 0; 
    boolean connected = false; 
    while (retryCount < 2 && !connected) { 
     try { 
      connection = DriverManager.getConnection(d.getUrl(), username, password); 
      LOG.info("Connected successfully to [{}]", dbName); 
      connected = true; 
     } catch (SQLException e) { 
      LOG.warn("Error: [{}] occured. Going to retry, attempt # [{}]", e.getMessage(), (retryCount + 1)); 
      retryCount++; 
      connected = false; 
     } 
    } 
    return connection; 
} 
+0

如果你不喜歡我的建議,你也可以在你的類中引入一個ConnectionProvider接口,它具有getConnection()。然後你可以模擬ConnectionProvider。 –

+0

它可能與這樣一個事實有關,即這是一個由bootstrap或系統類加載器加載的具體類。 –

回答

0

您可能會改用javax.sql.DataSource中,而不是使用的DriverManager的。

http://docs.oracle.com/javase/6/docs/api/javax/sql/DataSource.html

這樣,您與您可以模擬接口工作。現在大多數應用程序都使用DataSource接口,並且不像這樣與DriverManager交互。

所以,你的代碼可能會再有一個API,如:

public class DataSourceFactory { 
    public DataSource getDataSource(String url, String usr, String pwd); 
} 

而且你可以使用的DriverManager實現真正的數據源,如果你願意的話,然後你的單元測試模擬DataSourceFactory。

+0

謝謝。這總是一個選擇。即使當我創建connectionfactory。我可能需要使用Drivermanager。問題依然存在 - 爲什麼我們不能嘲笑Drivermanager?我試圖壓制靜態塊,但沒有使用:( – nav

+0

「爲什麼我們不能模擬DriverManager?」因爲Powermock在其ClassLoader中執行了一些高級字節碼操作,但仍有一些Java系統類/包在PowerMock可以觸摸之前加載他們。java.sql。*是其中之一。請參閱此主題(https://groups.google.com/forum/#!topic/powermock/6WqENatIqQU)和此SO問題(http://stackoverflow.com/questions/19386542/cant-suppress-drivermanagers-static-initializer-block)你也可以在模擬系統類(https://code.google.com/p/powermock/wiki/MockSystem)上查看這個頁面。 –

0

如果您不能模擬DriverManager.getConnection靜態調用,可能的解決方法就是跳過它。

將靜態調用提取到您的sut中的受保護方法,並使用與靜態方法相同的簽名。現在

protected Connection driverManagerGetConnection(String url, String user, String password){ 
    return DriverManager.getConnection(url, user, password); 
} 

,在您的測試,使用您的SUT的EasyMock to create a partial mock,嘲諷driverManagerGetConnection方法。

public void testFoo{ 

    // setup: sut 
    YourSutClass sut = EasyMock.createMockBuilder(YourSutClass.class) 
       .addMockedMethod("driverManagerGetConnection").createMock(); 

    ... 

    // setup: expectations 
    EasyMock.expect(sut.driverManagerGetConnection()).andReturn(mockConnection); 
    ... 

    // exercise 
    EasyMock.replay(sut, mockConnection); 
    sut.whatever(); 

    // verify 
    EasyMock.verify(sut, mockConnection); 
} 

使用這種方法,您唯一沒有測試的行是對靜態方法的調用。