【问题标题】:Using Mockito and PowerMockito for DAO testing使用 Mockito 和 PowerMock 进行 DAO 测试
【发布时间】:2015-08-14 15:11:47
【问题描述】:

我想使用 Mockito(如果需要,还可以使用 PowerMockito)测试我的 DAO 方法,但我不知道该怎么做。调用静态方法的最大问题(MySQLStationDAO 中的 MySQLDAOFactory.getConnection())。你能帮助我吗?

我通过这种方式获得连接:

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;
    }

【问题讨论】:

  • 你的问题到底是什么?你有例外吗?
  • 我们可能还需要一些额外的信息。您的数据源在哪里配置?

标签: java mockito dao powermockito


【解决方案1】:
  1. JUnit:在类级别使用@RunWith(PowerMockRunner.class)

    TestNG:让您的测试类扩展 PowerMockTestCase

  2. 在类级别使用 @PrepareForTest(MySQLDAOFactory.class) 以指示 PowerMock 准备 MySQLDAOFactory 类进行测试。

  3. 使用PowerMockito.mockStatic(MySQLDAOFactory.class) 以便 模拟MySQLDAOFactory类的所有方法。

    也可以使用partial mocking

    PowerMockito.stub(PowerMockito.method(MySQLDAOFactory.class, "getConnection")).toReturn(Mockito.mock(Connection.class));

  4. 使用类似的东西来存根getConnection()

    Connection mockConnection = Mockito.mock(Connection.class); Mockito.when(MySQLDAOFactory.getConnection()).thenReturn(mockConnection);

  5. MySQLStationDAO真实实例上执行getAllStations(),因为您正在测试MySQLStationDAO 类。

  6. 如果要验证 getConnection() 方法是否已被调用,请使用类似的方法:

    PowerMockito.verifyStatic(); MySQLDAOFactory.getConnection();

    但是,请阅读Mockito.verify(T) javadoc,了解为什么建议存根或验证调用的原因,而不是两者兼而有之。

一般来说,您可能需要咨询Mockito docsPowerMockito 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 数据集验证返回的结果。

【讨论】:

    【解决方案2】:

    正如你所说,你的问题是当你调用 MySQLDAOFactory.getConnection();在测试方面,您想要测试您的 MySQLStationDAO 类。这是您的 SUT(被测系统)。这意味着您必须模拟您的 SUT 拥有的所有依赖项。在这种情况下,MySQLDAOFactory。

    为此,您可以使用 Mockito 轻松模拟该类并存根 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 类,这是您要测试的类。

    【讨论】:

    • 请注意getConnection() 方法是静态的。您的答案仅适用于实例方法。
    • 哦,是的。我没看到。然后 Mockito 是不够的,因为你不能模拟静态方法。在这种情况下,同意给定的 asnwer
    猜你喜欢
    • 2020-01-17
    • 2021-03-15
    • 2016-03-15
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多