Mock Dynamic QueryAsync触发Dapper空引用异常,求排查方案
我在编写数据库连接的单元测试Mock代码时遇到了空引用异常,以下是我的Mock代码:
var connection = new Mock<IDatabaseConnectionFactory>(); var conn = new Mock<IDbConnection>(); connection.Setup(c => c.GetConnection()).Returns(conn.Object); _productRepository = new ProductRepository(connection.Object, _httpContextAccessorMock.Object); var listDyn = new List<dynamic>(); listDyn.Add(1); conn.SetupDapperAsync(c => c.QueryAsync(It.IsAny<string>(), It.IsAny<object>(), It.IsAny<IDbTransaction>(), It.IsAny<int>(), It.IsAny<CommandType>())) .ReturnsAsync(listDyn.AsEnumerable()); var model = new Model() { name = "test", owner = "owner "}; var result = _productRepository.AddProduct(model);
调用AddProduct(model)时,在这段Dapper调用代码处触发错误:
await connection.QueryAsync("[dbo].[storedProcName]", param: parameters, commandType: CommandType.StoredProcedure).ConfigureAwait(false)
错误信息:
Message: "Object reference not set to an instance of an object."
Source: "Dapper"
堆栈跟踪:
Object reference not set to an instance of an object. at Dapper.DynamicParameters.AddParameters(IDbCommand command, Identity identity)
at Dapper.DynamicParameters.Dapper.SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, Identity identity)
at Dapper.SqlMapper.<>c__DisplayClass165_0.b__0(IDbCommand cmd, Object obj)
at Dapper.CommandDefinition.SetupCommand(IDbConnection cnn, Action2 paramReader) at Dapper.SqlMapper.<QueryAsync>d__331.MoveNext()
at Repository.Repositories.VendorRepository.d__13.MoveNext() in C:\Users\xxxxx\Documents\xxx\Projects\xxxApi\Infrastructure\Repository\Repositories\ProductRepository.cs:line 168
请问我是不是遗漏了什么配置?
问题原因
空引用异常来自Dapper的DynamicParameters.AddParameters方法,说明Mock过程中Dapper需要操作的IDbCommand对象未正确初始化——你只Mock了QueryAsync方法,但Dapper执行查询时会创建IDbCommand并尝试添加参数,而Mock的IDbConnection没有配置CreateCommand方法,导致返回的IDbCommand为null,进而触发空引用。
解决步骤
Mock IDbCommand及必要成员
需要创建IDbCommand的Mock实例,至少配置CreateParameter方法(Dapper添加参数时会用到),同时确保IDbConnection的CreateCommand返回这个Mock的Command对象。更新后的Mock代码
var connection = new Mock<IDatabaseConnectionFactory>(); var conn = new Mock<IDbConnection>(); // 新增IDbCommand的Mock实例 var cmdMock = new Mock<IDbCommand>(); // 新增IDbDataParameter的Mock实例(Dapper添加参数依赖此对象) var paramMock = new Mock<IDbDataParameter>(); // 配置Connection的CreateCommand返回Mock的Command conn.Setup(c => c.CreateCommand()).Returns(cmdMock.Object); // 配置Command的CreateParameter返回Mock的参数 cmdMock.Setup(c => c.CreateParameter()).Returns(paramMock.Object); connection.Setup(c => c.GetConnection()).Returns(conn.Object); _productRepository = new ProductRepository(connection.Object, _httpContextAccessorMock.Object); var listDyn = new List<dynamic>(); listDyn.Add(1); conn.SetupDapperAsync(c => c.QueryAsync(It.IsAny<string>(), It.IsAny<object>(), It.IsAny<IDbTransaction>(), It.IsAny<int>(), It.IsAny<CommandType>())) .ReturnsAsync(listDyn.AsEnumerable()); var model = new Model() { name = "test", owner = "owner "}; // 注意:AddProduct是异步方法,必须用await等待执行完成 var result = await _productRepository.AddProduct(model);额外注意事项
- 原代码中调用
AddProduct时未使用await,如果该方法是异步的,会导致任务未完成就继续执行,可能引发其他问题,务必加上await。 - 如果你的存储过程需要处理特定参数逻辑,可能需要进一步Mock
IDbCommand的Parameters集合,但基础场景下以上配置已能解决空引用问题。
- 原代码中调用
内容的提问来源于stack exchange,提问作者choopau

