2013-10-27 109 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); 
} 

使用这种方法,您唯一没有测试的行是对静态方法的调用。