首页 > 解决方案 > 使用 Mockito 模拟结果集

问题描述

如何模拟结果集?

在测试类中尝试如下模拟结果集,但是,当尝试测试UnnecessaryStubbingException在语句中出现错误时:

voObj.setDept(rs.getString(2)); 

voObj.setDeptDesc(rs.getString(3));

关于如何模拟结果集的任何建议?

public class Example { 
    public static void main(String[] s) {
        method1();
        method2();
        ..........
    }

    private Employee method1(String str) {
                Connection conn           =  getConnection();   
        PreparedStatement pstmt   = null;
        .........
        pstmt = conn.prepareStatement(strQuery.toString());
        rs  = pstmt.executeQuery();
        int ilCounter   = 0; 
        int maxId   = method2(loc); //some DB calls here with select

        if(null != rs) {            
            while(rs.next()) { 
                ilCounter++;
                ObjVoBean voObj = new ObjVoBean();
                voObj.setLoc(rs.getString(1));
                voObj.setDept(rs.getString(2));
                voObj.setDeptDesc(rs.getString(3));
            }
            .................
        }
       }

   private Employee method2(String str1) {
                Connection connOHM      = getConnection();  
        PreparedStatement pstmt     = null;
        .........
       //some DB call with select ...
   }
}

public class ExampleTest { 
   @InjectMocks
   Example example;

   @Mock
   private Connection c;

   @Mock
   private PreparedStatement preStmt;
    .....

   @Before
   public void setUp() {
        ........
   }

   @Test
   public void testMethod1() throws SQLException {
        ResultSet resultSetMock = Mockito.mock(ResultSet.class);
        when(resultSetMock.getString(1)).thenReturn("1111");
        when(resultSetMock.getString(2)).thenReturn("2222");
        when(resultSetMock.getString(3)).thenReturn("dept desc");

        when(c.prepareStatement(any(String.class))).thenReturn(preStmt);
        when(resultSetMock.next()).thenReturn(true).thenReturn(false); 
        doReturn(resultSetMock).when(preStmt).executeQuery();

        example.method1("1111");            
        assertTrue(true);
   }
}

标签: javajunitmockito

解决方案


为了能够模拟ResultSet你应该模拟所有允许创建它的对象,即创建它ConnectionPreparedStatement它本身创建ResultSet。仅当您提供一种从客户端代码设置连接的方法时,模拟连接才会在测试代码中起作用。

conn是 Connection 应该首先作为依赖项注入到您的测试夹具中:

pstmt = conn.prepareStatement(strQuery.toString());

通常,您创建一个Connection例如:

conn = DriverManager.getConnection(DB_URL,USER,PASS);

或通过DataSource诸如:

conn = ds.getConnection();

因此,您应该将此部分抽象为接口或非最终类,并定义执行此处理的实现。通过这种方式,您可以模拟创建连接的部分。所以你可以模拟整个链:Connection-PreparedStatement-ResultSet。


我个人会避免这种方式,因为嘲笑太多东西通常不是正确的选择。
在您的情况下,您需要在加载 ResultSet 后模拟 ResultSet 以测试后处理:

while(rs.next()) { 
     ilCounter++;
     ObjVoBean voObj = new ObjVoBean();
     voObj.setLoc(rs.getString(1));
     voObj.setDept(rs.getString(2));
     voObj.setDeptDesc(rs.getString(3));
}

因此,作为替代方案,您可以将之前执行的所有代码移动到处理持久性部分的特定类的方法中。这样你只需要模拟这个依赖和这个方法。您无需担心 Connection 和任何 JDBC 细节。

EmployeeDAO employeeDAO; // dependency to mock

// constructor with dependency
public Example(EmployeeDAO employeeDAO){
  this.employeeDAO = employeeDAO;
}

private Employee method1(String str) {
   ResultSet resultSet = employeeDAO.load(str);

    if(null != rs) {            
        while(rs.next()) { 
            ilCounter++;
            ObjVoBean voObj = new ObjVoBean();
            voObj.setLoc(rs.getString(1));
            voObj.setDept(rs.getString(2));
            voObj.setDeptDesc(rs.getString(3));
        }
        .................
    }
   }

当然,DAO 组件也必须进行单一测试。
Bu 如前所述,断言 aConnection已创建或它返回 aPreparedStatement不会带来价值。在功能覆盖方面测试您的查询是否执行您对它们的期望时会更有趣。
在这种情况下,您希望针对 H2 等内存数据库对其进行测试,因为单元测试不是集成测试,并且必须快速执行单元测试。
要编写 DAO/Repository 测试,DbunitDbSetup是很好的候选者,因为它们提供了在每次测试之前设置 DB 的工具(主要是注入数据和清除数据)。


推荐阅读