You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 06:27:18