尽管可以通过静态方法访问,但如何模拟数据源依赖注入
How to Mock DataSource Dependency Injection Despite Being Accessible via Static Method
我正在使用 Mockito, DBUnit and HSQLDB 对我的数据库代码进行单元测试。我当然也在写集成测试。
我无法理解如何将模拟的 DataSource 注入被测系统(class 我正在测试)。 DataSource
用于连接池,因此其他 classes 可以调用同一 class 中的 static
方法来检索此 DataSource
的实例].这意味着 DataSource
不会在任何地方注入任何构造函数,因此我的测试没有任何构造函数可以将模拟的 DataSource
注入其中。
我通过改变我的真实代码的逻辑来解决这个问题,以检查私有变量是否为空,如果是,则使用注入的数据源(糟糕的设计,因为它只用于测试),否则它调用检索连接池源的静态方法(更好的设计)。
我如何将模拟的 DataSource
注入到没有设置接受它的构造函数的 class 中,因为它只能调用静态方法来检索依赖项?
Class 测试
public DBConnection(DBSource dbSource) { // <--- Constructor only required for test purposes :(
this.dbSource = dbSource;
}
public final void createCompsDB() {
Connection conn = null;
Statement statement = null;
try {
if(dbSource==null){
conn = DBSource.getInstance().getConnection();
}else{
conn = dbSource.getConnection(); /** Likely bad design, since dbSource is only NOT null for tests, so that you can inject the mocked datasource :( */
}
statement = conn.createStatement();
statement.executeUpdate("CREATE DATABASE placesdb");
System.out.println("Database created...");
} catch (SQLException e) {
// ...
}
} finally {
// Close Resources...
}
}
}
测试Class -- 测试通过
public class DBConnectionTest {
final Statement statement = mock(Statement.class);
final Connection connection = mock(Connection.class);
final DBSource dataSource = mock(DBSource.class);
@Before
public void setUp() throws SQLException, IOException, PropertyVetoException {
when(dataSource.getConnection()).thenReturn(connection);
when(connection.createStatement()).thenReturn(statement);
}
@Test
public void testCreateCompDBIfNotAlready() throws Exception {
DBConnection dbConnection = new DBConnection(localDB, dataSource); /** This constructor is only needed for testing :( . How do I avoid it since all the classes I need to test don't require the dependency to be injected? */
dbConnection.createCompsDB();
verify(statement).executeUpdate("CREATE DATABASE PLACES");
}
}
DBSource.java
protected DBSource() throws IOException, SQLException, PropertyVetoException {
ds = new BasicDataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUsername("user");
ds.setPassword("pass");
ds.setUrl("jdbc:postgresql://localhost:5432/placesdb");
}
public static DBSource getInstance() { // <--- Static method means dependent classes don't need to accept injections
if (datasource == null) {
datasource = new DBSource();
return datasource;
} else {
return datasource;
}
}
public Connection getConnection() throws SQLException {
return this.ds.getConnection();
}
}
静态 class 方法的模拟可以使用 PowerMockito 完成。
测试 class 应该是这样的:
@RunWith(PowerMockRunner.class)
@PrepareForTest(DBSource.class)
public class DBConnectionTest {
@Mock
final Statement statement;
@Mock
final Connection connection;
@Mock
final DBSource dbsource;
@Before
public void setUp() throws SQLException, IOException, PropertyVetoException {
PowerMockito.mockStatic(DBSource.class);
when(DbSource.getInstance()).thenReturn(dbsource);
when(dbsource.getConnection()).thenReturn(connection);
when(connection.createStatement()).thenReturn(statement);
}
@Test
public void testCreateCompDBIfNotAlready() throws Exception {
DBConnection dbConnection = new DBConnection(localDB); // No test-only constructor anymore
dbConnection.createCompsDB();
verify(statement).executeUpdate("CREATE DATABASE PLACES");
}
}
您可以阅读 here 更多关于使用 PowerMock 模拟的信息。
我正在使用 Mockito, DBUnit and HSQLDB 对我的数据库代码进行单元测试。我当然也在写集成测试。
我无法理解如何将模拟的 DataSource 注入被测系统(class 我正在测试)。 DataSource
用于连接池,因此其他 classes 可以调用同一 class 中的 static
方法来检索此 DataSource
的实例].这意味着 DataSource
不会在任何地方注入任何构造函数,因此我的测试没有任何构造函数可以将模拟的 DataSource
注入其中。
我通过改变我的真实代码的逻辑来解决这个问题,以检查私有变量是否为空,如果是,则使用注入的数据源(糟糕的设计,因为它只用于测试),否则它调用检索连接池源的静态方法(更好的设计)。
我如何将模拟的 DataSource
注入到没有设置接受它的构造函数的 class 中,因为它只能调用静态方法来检索依赖项?
Class 测试
public DBConnection(DBSource dbSource) { // <--- Constructor only required for test purposes :(
this.dbSource = dbSource;
}
public final void createCompsDB() {
Connection conn = null;
Statement statement = null;
try {
if(dbSource==null){
conn = DBSource.getInstance().getConnection();
}else{
conn = dbSource.getConnection(); /** Likely bad design, since dbSource is only NOT null for tests, so that you can inject the mocked datasource :( */
}
statement = conn.createStatement();
statement.executeUpdate("CREATE DATABASE placesdb");
System.out.println("Database created...");
} catch (SQLException e) {
// ...
}
} finally {
// Close Resources...
}
}
}
测试Class -- 测试通过
public class DBConnectionTest {
final Statement statement = mock(Statement.class);
final Connection connection = mock(Connection.class);
final DBSource dataSource = mock(DBSource.class);
@Before
public void setUp() throws SQLException, IOException, PropertyVetoException {
when(dataSource.getConnection()).thenReturn(connection);
when(connection.createStatement()).thenReturn(statement);
}
@Test
public void testCreateCompDBIfNotAlready() throws Exception {
DBConnection dbConnection = new DBConnection(localDB, dataSource); /** This constructor is only needed for testing :( . How do I avoid it since all the classes I need to test don't require the dependency to be injected? */
dbConnection.createCompsDB();
verify(statement).executeUpdate("CREATE DATABASE PLACES");
}
}
DBSource.java
protected DBSource() throws IOException, SQLException, PropertyVetoException {
ds = new BasicDataSource();
ds.setDriverClassName("org.postgresql.Driver");
ds.setUsername("user");
ds.setPassword("pass");
ds.setUrl("jdbc:postgresql://localhost:5432/placesdb");
}
public static DBSource getInstance() { // <--- Static method means dependent classes don't need to accept injections
if (datasource == null) {
datasource = new DBSource();
return datasource;
} else {
return datasource;
}
}
public Connection getConnection() throws SQLException {
return this.ds.getConnection();
}
}
静态 class 方法的模拟可以使用 PowerMockito 完成。 测试 class 应该是这样的:
@RunWith(PowerMockRunner.class)
@PrepareForTest(DBSource.class)
public class DBConnectionTest {
@Mock
final Statement statement;
@Mock
final Connection connection;
@Mock
final DBSource dbsource;
@Before
public void setUp() throws SQLException, IOException, PropertyVetoException {
PowerMockito.mockStatic(DBSource.class);
when(DbSource.getInstance()).thenReturn(dbsource);
when(dbsource.getConnection()).thenReturn(connection);
when(connection.createStatement()).thenReturn(statement);
}
@Test
public void testCreateCompDBIfNotAlready() throws Exception {
DBConnection dbConnection = new DBConnection(localDB); // No test-only constructor anymore
dbConnection.createCompsDB();
verify(statement).executeUpdate("CREATE DATABASE PLACES");
}
}
您可以阅读 here 更多关于使用 PowerMock 模拟的信息。