使用 Mockito 和 PowerMockito 进行 DAO 测试
Using Mockito and PowerMockito for DAO testing
我想使用 Mockito(如果需要,还可以使用 PowerMockito)测试我的 DAO 方法,但我不知道该怎么做。调用静态方法的最大问题(MySQLDAOFactory.getConnection() in MySQLStationDAO)。你能帮助我吗?
我是这样获取连接的:
public class MySQLDAOFactory extends DAOFactory {
public static Connection getConnection() throws DAOException {
Connection con = null;
try {
con = getDataSource().getConnection();
} catch (SQLException e) {
throw new DAOException(Messages.CANNOT_OBTAIN_CONNECTION, e);
}
return con;
}
这是一个 DAO 方法:
public class MySQLStationDAO implements StationDAO {
@Override
public List<Station> getAllStations() throws DAOException {
List<Station> stations = new ArrayList<>();
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
con = MySQLDAOFactory.getConnection();
stmt = con.createStatement();
rs = stmt.executeQuery(MySQLQueries.SQL_GET_ALL_STATIONS);
while (rs.next()) {
stations.add(extractStation(rs));
}
} catch (SQLException e) {
throw new DAOException(Messages.CANNOT_OBTAIN_ALL_STATIONS, e);
} finally {
MySQLDAOFactory.close(con, stmt, rs);
}
return stations;
}
正如您所说,您的问题是在调用 MySQLDAOFactory.getConnection() 时;在测试方面,您想测试您的 MySQLStationDAO class。这是您的 SUT(被测系统)。这意味着您必须模拟 SUT 具有的所有依赖项。在这种情况下,MySQLDAOFactory。
为此,您可以使用 Mockito 轻松模拟 class 并存根 MySQLDAOFactory 提供的方法。一个例子是
package com.iseji.app.dao;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import java.sql.Connection;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class TestMySqlDaoFactory {
MySqlDaoFactory mySqlDaoFactory;
Connection connection;
@Before
public void setUp() throws DAOException {
mySqlDaoFactory = mock(MySqlDaoFactory.class);
connection = mock(Connection.class);
}
@Test(expected = DAOException.class)
public void testEmptyUrlGetsDaoException() throws DAOException {
when(mySqlDaoFactory.getConnection(null)).thenThrow(new DAOException());
mySqlDaoFactory.getConnection(null);
}
@Test
public void testFullUrlGetsConnection() throws DAOException {
when(mySqlDaoFactory.getConnection(anyString())).thenReturn(connection);
Assert.assertEquals(mySqlDaoFactory.getConnection(anyString()), connection);
}
}
如您所见,您可以指定 DaoFactory 的行为。这将隔离你的 Dao class,这是你想要测试的 class。
JUnit:在 class 级使用 @RunWith(PowerMockRunner.class)
。
TestNG:使您的测试 class 扩展 PowerMockTestCase
.
在 class 级使用 @PrepareForTest(MySQLDAOFactory.class)
以指示 PowerMock 准备 MySQLDAOFactory
class 进行测试。
使用PowerMockito.mockStatic(MySQLDAOFactory.class)
为了
模拟 MySQLDAOFactory
class.
的所有方法
也可以使用partial mocking:
PowerMockito.stub(PowerMockito.method(MySQLDAOFactory.class, "getConnection")).toReturn(Mockito.mock(Connection.class));
使用类似的东西来存根 getConnection()
:
Connection mockConnection = Mockito.mock(Connection.class);
Mockito.when(MySQLDAOFactory.getConnection()).thenReturn(mockConnection);
在 MySQLStationDAO
的 真实实例 上执行 getAllStations()
,因为您正在测试 MySQLStationDAO
class.
如果要验证 getConnection()
方法是否已被调用,请使用类似的东西:
PowerMockito.verifyStatic();
MySQLDAOFactory.getConnection();
但是,请阅读 Mockito.verify(T) javadoc,了解为什么建议存根或验证调用,而不是两者。
一般来说,您可能需要参考 Mockito docs and PowerMockito docs 了解更多信息。
使用 JUnit 4.11、Mockito 1.9.5 和 PowerMock (PowerMockito) 1.5.6 创建的完整示例(请注意版本,因为存在很多兼容性问题):
@RunWith(PowerMockRunner.class)
@PrepareForTest(MySQLDAOFactory.class)
public class MySQLDAOFactoryTest {
private StationDAO stationDAO;
@Mock
private Connection mockConnection;
@Mock
private Statement mockStatement;
@Mock
private ResultSet mockResultSet;
@Before
public void setUp() {
stationDAO = new MySQLStationDAO();
}
@Test
public void testGetAllStations_StatementCreated() throws DAOException, SQLException {
// given
PowerMockito.mockStatic(MySQLDAOFactory.class);
Mockito.when(MySQLDAOFactory.getConnection()).thenReturn(mockConnection);
Mockito.when(mockConnection.createStatement()).thenReturn(mockStatement);
Mockito.when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet);
// when
stationDAO.getAllStations();
// then
Mockito.verify(mockConnection).createStatement();
}
}
下一步是什么?检查是否已使用预期参数调用了 executeQuery()
方法?测试SQLException
是如何处理的?这些是单元测试的合理场景,但是集成测试呢?为此,我建议使用 DBUnit。它将您的测试数据库置于测试运行之间的已知状态,并允许根据预期的 XML 数据集验证返回的结果。
我想使用 Mockito(如果需要,还可以使用 PowerMockito)测试我的 DAO 方法,但我不知道该怎么做。调用静态方法的最大问题(MySQLDAOFactory.getConnection() in MySQLStationDAO)。你能帮助我吗?
我是这样获取连接的:
public class MySQLDAOFactory extends DAOFactory {
public static Connection getConnection() throws DAOException {
Connection con = null;
try {
con = getDataSource().getConnection();
} catch (SQLException e) {
throw new DAOException(Messages.CANNOT_OBTAIN_CONNECTION, e);
}
return con;
}
这是一个 DAO 方法:
public class MySQLStationDAO implements StationDAO {
@Override
public List<Station> getAllStations() throws DAOException {
List<Station> stations = new ArrayList<>();
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
con = MySQLDAOFactory.getConnection();
stmt = con.createStatement();
rs = stmt.executeQuery(MySQLQueries.SQL_GET_ALL_STATIONS);
while (rs.next()) {
stations.add(extractStation(rs));
}
} catch (SQLException e) {
throw new DAOException(Messages.CANNOT_OBTAIN_ALL_STATIONS, e);
} finally {
MySQLDAOFactory.close(con, stmt, rs);
}
return stations;
}
正如您所说,您的问题是在调用 MySQLDAOFactory.getConnection() 时;在测试方面,您想测试您的 MySQLStationDAO class。这是您的 SUT(被测系统)。这意味着您必须模拟 SUT 具有的所有依赖项。在这种情况下,MySQLDAOFactory。
为此,您可以使用 Mockito 轻松模拟 class 并存根 MySQLDAOFactory 提供的方法。一个例子是
package com.iseji.app.dao;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;
import java.sql.Connection;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class TestMySqlDaoFactory {
MySqlDaoFactory mySqlDaoFactory;
Connection connection;
@Before
public void setUp() throws DAOException {
mySqlDaoFactory = mock(MySqlDaoFactory.class);
connection = mock(Connection.class);
}
@Test(expected = DAOException.class)
public void testEmptyUrlGetsDaoException() throws DAOException {
when(mySqlDaoFactory.getConnection(null)).thenThrow(new DAOException());
mySqlDaoFactory.getConnection(null);
}
@Test
public void testFullUrlGetsConnection() throws DAOException {
when(mySqlDaoFactory.getConnection(anyString())).thenReturn(connection);
Assert.assertEquals(mySqlDaoFactory.getConnection(anyString()), connection);
}
}
如您所见,您可以指定 DaoFactory 的行为。这将隔离你的 Dao class,这是你想要测试的 class。
JUnit:在 class 级使用
@RunWith(PowerMockRunner.class)
。TestNG:使您的测试 class 扩展
PowerMockTestCase
.在 class 级使用
@PrepareForTest(MySQLDAOFactory.class)
以指示 PowerMock 准备MySQLDAOFactory
class 进行测试。使用
的所有方法PowerMockito.mockStatic(MySQLDAOFactory.class)
为了 模拟MySQLDAOFactory
class.也可以使用partial mocking:
PowerMockito.stub(PowerMockito.method(MySQLDAOFactory.class, "getConnection")).toReturn(Mockito.mock(Connection.class));
使用类似的东西来存根
getConnection()
:Connection mockConnection = Mockito.mock(Connection.class); Mockito.when(MySQLDAOFactory.getConnection()).thenReturn(mockConnection);
在
MySQLStationDAO
的 真实实例 上执行getAllStations()
,因为您正在测试MySQLStationDAO
class.如果要验证
getConnection()
方法是否已被调用,请使用类似的东西:PowerMockito.verifyStatic(); MySQLDAOFactory.getConnection();
但是,请阅读 Mockito.verify(T) javadoc,了解为什么建议存根或验证调用,而不是两者。
一般来说,您可能需要参考 Mockito docs and PowerMockito docs 了解更多信息。
使用 JUnit 4.11、Mockito 1.9.5 和 PowerMock (PowerMockito) 1.5.6 创建的完整示例(请注意版本,因为存在很多兼容性问题):
@RunWith(PowerMockRunner.class)
@PrepareForTest(MySQLDAOFactory.class)
public class MySQLDAOFactoryTest {
private StationDAO stationDAO;
@Mock
private Connection mockConnection;
@Mock
private Statement mockStatement;
@Mock
private ResultSet mockResultSet;
@Before
public void setUp() {
stationDAO = new MySQLStationDAO();
}
@Test
public void testGetAllStations_StatementCreated() throws DAOException, SQLException {
// given
PowerMockito.mockStatic(MySQLDAOFactory.class);
Mockito.when(MySQLDAOFactory.getConnection()).thenReturn(mockConnection);
Mockito.when(mockConnection.createStatement()).thenReturn(mockStatement);
Mockito.when(mockStatement.executeQuery(anyString())).thenReturn(mockResultSet);
// when
stationDAO.getAllStations();
// then
Mockito.verify(mockConnection).createStatement();
}
}
下一步是什么?检查是否已使用预期参数调用了 executeQuery()
方法?测试SQLException
是如何处理的?这些是单元测试的合理场景,但是集成测试呢?为此,我建议使用 DBUnit。它将您的测试数据库置于测试运行之间的已知状态,并允许根据预期的 XML 数据集验证返回的结果。