如何为QueryMultipleAsync编写测试用例?遇NotSupportedException报错
问题根源
错误System.NotSupportedException的核心原因是:Dapper的SqlMapper.GridReader是密封类,且ReadAsync方法不是虚方法。Moq仅能Mock接口或包含虚/抽象方法的类,直接对GridReader做Mock会触发该异常。
解决方案
方法一:封装Dapper操作到自定义接口(推荐)
通过适配器模式封装Dapper的底层操作,让数据访问层依赖自定义接口,而非直接依赖IDbConnection和GridReader,实现可Mock的能力。
- 定义封装接口
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重载 }
- 实现接口
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); } }
- 修改数据访问层代码
调整依赖并适配新接口:
// 构造注入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; }
- 测试中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
相关产品推荐
相关产品推荐

