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

如何为QueryMultipleAsync编写测试用例?遇NotSupportedException报错

问题根源

错误System.NotSupportedException的核心原因是:Dapper的SqlMapper.GridReader是密封类,且ReadAsync方法不是虚方法。Moq仅能Mock接口或包含虚/抽象方法的类,直接对GridReader做Mock会触发该异常。

解决方案

方法一:封装Dapper操作到自定义接口(推荐)

通过适配器模式封装Dapper的底层操作,让数据访问层依赖自定义接口,而非直接依赖IDbConnection和GridReader,实现可Mock的能力。

  1. 定义封装接口
public interface IDapperService
{
    Task<IGridReaderWrapper> QueryMultipleAsync(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null);
}

public interface IGridReaderWrapper
{
    Task<IEnumerable<T>> ReadAsync<T>(bool buffered = true);
    // 按需添加其他ReadAsync重载
}
  1. 实现接口
public class DapperService : IDapperService
{
    private readonly IDbConnection _connection;

    public DapperService(IDbConnection connection)
    {
        _connection = connection;
    }

    public async Task<IGridReaderWrapper> QueryMultipleAsync(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
    {
        var gridReader = await _connection.QueryMultipleAsync(sql, param, transaction, commandTimeout, commandType);
        return new GridReaderWrapper(gridReader);
    }
}

public class GridReaderWrapper : IGridReaderWrapper
{
    private readonly GridReader _gridReader;

    public GridReaderWrapper(GridReader gridReader)
    {
        _gridReader = gridReader;
    }

    public async Task<IEnumerable<T>> ReadAsync<T>(bool buffered = true)
    {
        return await _gridReader.ReadAsync<T>(buffered);
    }
}
  1. 修改数据访问层代码
    调整依赖并适配新接口:
// 构造注入IDapperService
public YourDataAccessClass(IDapperService dapperService, IContext context)
{
    _dapperService = dapperService;
    _context = context;
}

public async Task<bool> SummerData(Guid darkStoreId, Guid storeId, long id)
{
    await _context.OpenConnectionForKeyAsync(darkStoreId);
    var result = await _dapperService.QueryMultipleAsync(_checkMappingStatus, new { storeId, id });
    return await DataStocks(result);
}

// 修改方法参数为自定义接口
private async Task<bool> DataStocks(IGridReaderWrapper result)
{
    var data = await result.ReadAsync<YourModelType>();
    // 原有业务逻辑
    return true;
}
  1. 测试中Mock接口
public void SetupQueryMultipleAsync(IEnumerable<YourModelType> expected)
{
    var mockGridReader = new Mock<IGridReaderWrapper>();
    mockGridReader.Setup(a => a.ReadAsync<YourModelType>(It.IsAny<bool>()))
        .ReturnsAsync(expected);

    var mockDapperService = new Mock<IDapperService>();
    mockDapperService.Setup(s => s.QueryMultipleAsync(It.IsAny<string>(), It.IsAny<object>(), It.IsAny<IDbTransaction>(), It.IsAny<int?>(), It.IsAny<CommandType?>()))
        .ReturnsAsync(mockGridReader.Object);

    // 注入Mock对象到测试类
    data = new YourDataAccessClass(mockDapperService.Object, mockContext.Object);
}

方法二:使用内存数据库生成真实GridReader

直接用SQLite内存数据库执行真实查询,获取真实的GridReader,避免Mock操作:

[Fact]
public async Task SummerData()
{
    // 创建SQLite内存连接
    using var connection = new SqliteConnection("Data Source=:memory:");
    connection.Open();

    // 初始化测试表和数据
    connection.Execute("CREATE TABLE YourTable (id BIGINT, tenantClientId UNIQUEIDENTIFIER)");
    connection.Execute("INSERT INTO YourTable VALUES (@id, @tenantClientId)", new { id = 1L, tenantClientId });

    // 替换数据访问类的连接为真实内存连接
    data._context.Connection = connection;

    // 执行测试方法
    var result = await data.SummerData(darkStoreId, storeId, 1);

    // 断言结果
    Assert.True(result);
}

方法三:非公共成员拦截(不推荐)

可通过Moq的Protected()API或第三方库拦截非虚方法,但该方式依赖Dapper内部实现,易因版本更新失效,仅作为临时方案。

内容的提问来源于stack exchange,提问作者pixel4

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:56:05