如何使用Moq模拟IDataReader.NextResult实现多结果集测试
Moq模拟多结果集DataReader配置方法
方案1:通过DataSet生成多结果集Reader(推荐,零额外Mock成本)
你不需要手动编写NextResult()的模拟逻辑,.NET原生提供的DataSet.CreateDataReader()方法天然支持多结果集遍历,和真实数据库返回的DataReader行为完全一致,和你现有单结果集的测试代码兼容性最好。
只需要把对应顺序的测试DataTable加入DataSet,再生成Reader即可:
// 构造第一个结果集的测试DataTable(和你之前单结果集的构造逻辑完全一致) DataTable firstResultTable = new DataTable(); firstResultTable.Columns.Add("Id", typeof(int)); firstResultTable.Columns.Add("UserName", typeof(string)); firstResultTable.Rows.Add(1, "张三"); firstResultTable.Rows.Add(2, "李四"); // 构造第二个结果集的测试DataTable DataTable secondResultTable = new DataTable(); secondResultTable.Columns.Add("OrderId", typeof(int)); secondResultTable.Columns.Add("TotalAmount", typeof(decimal)); secondResultTable.Rows.Add(1001, 89.9m); // 将两个结果集按存储过程返回的顺序加入DataSet DataSet testDataSet = new DataSet(); testDataSet.Tables.Add(firstResultTable); testDataSet.Tables.Add(secondResultTable); // 生成支持NextResult切换的多结果集Reader IDataReader multiResultReader = testDataSet.CreateDataReader(); // 原有Mock配置无需修改,直接返回该Reader即可 commandMock.Setup(m => m.ExecuteReader()).Returns(multiResultReader);
使用这个方式生成的Reader,调用NextResult()时会自动按Tables的顺序切换结果集,遍历行为和生产环境的SqlDataReader完全一致,不需要额外配置任何Moq逻辑。
方案2:纯Moq手动模拟IDataReader行为(适合自定义测试场景)
如果你不想依赖DataTable/DataSet构造测试数据,也可以直接MockIDataReader接口的核心方法,手动控制结果集切换和行读取逻辑:
Mock<IDataReader> readerMock = new Mock<IDataReader>(); // 标记当前所在的结果集序号 int currentResultSet = 0; // 标记当前结果集内读取到的行序号 int currentRow = 0; // 配置Read()方法的返回逻辑,根据自己的测试数据行数调整判断条件 readerMock.Setup(r => r.Read()) .Returns(() => { currentRow++; // 示例:第一个结果集共2行,第二个结果集共1行 if (currentResultSet == 0 && currentRow <= 2) return true; if (currentResultSet == 1 && currentRow <= 1) return true; return false; }); // 配置NextResult()方法的切换逻辑 readerMock.Setup(r => r.NextResult()) .Returns(() => { currentResultSet++; currentRow = 0; // 示例:总共2个结果集,切换到不存在的结果集时返回false return currentResultSet < 2; }); // 按需配置字段读取逻辑,比如通过列序号取值 readerMock.Setup(r => r[It.IsAny<int>()]) .Returns((int colIndex) => { // 根据当前结果集、行号、列号返回对应的测试值即可 if (currentResultSet == 0 && currentRow == 1 && colIndex == 0) return 1; if (currentResultSet == 0 && currentRow == 1 && colIndex == 1) return "张三"; // 其余测试值按需求补全 return null; }); // 配置Command返回模拟的Reader commandMock.Setup(m => m.ExecuteReader()).Returns(readerMock.Object);
注意:纯Mock方案需要手动配置所有业务代码用到的IDataReader成员(比如
GetOrdinal、IsDBNull、GetString等方法),漏配会导致运行时返回默认值,容易和真实数据库Reader的行为出现偏差,非特殊场景优先选择第一种方案。
内容的提问来源于stack exchange,提问作者TimTheEnchanter
相关产品推荐
相关产品推荐

