如何使用Moq对C#泛型异步方法ExecuteAsync进行Mock与配置?
Itest接口ExecuteAsync方法的Mock配置方案
首先注意:原接口代码里的Public关键字需要改为小写的public,C#是大小写敏感语言,否则会编译报错。
以下是基于Moq框架的Mock配置方案,覆盖常见场景:
1. 基础Mock配置(匹配任意参数)
先创建接口的Mock实例:
var mockTest = new Mock<Itest>();
如果不需要严格校验参数,直接配置方法返回已完成的Task:
// 适配所有符合约束的TException泛型类型(Moq 4.13+支持) mockTest.Setup(m => m.ExecuteAsync( It.IsAny<Func<Task>>(), It.IsAny<Action<(Exception Exception, int X, int Total)>>(), It.IsAny<int?>())) .Returns(Task.CompletedTask); // 或者指定具体泛型类型 mockTest.Setup(m => m.ExecuteAsync<InvalidOperationException>( It.IsAny<Func<Task>>(), It.IsAny<Action<(Exception Exception, int X, int Total)>>(), It.IsAny<int?>())) .Returns(Task.CompletedTask);
2. 模拟触发传入的ontest回调
如果需要模拟方法执行时调用传入的ontest委托(比如模拟重试通知场景):
mockTest.Setup(m => m.ExecuteAsync<IOException>( It.IsAny<Func<Task>>(), It.IsAny<Action<(Exception Exception, int X, int Total)>>(), It.IsAny<int?>())) .Callback((Func<Task> action, Action<(Exception, int, int)> ontest, int? y) => { // 调用传入的回调,传递模拟的异常、当前重试次数、总次数 ontest?.Invoke((new IOException("模拟IO异常"), 1, 3)); }) .Returns(Task.CompletedTask);
3. 模拟执行传入的Func并处理异常
如果需要实际执行传入的action委托,或者模拟action抛出异常的场景:
mockTest.Setup(m => m.ExecuteAsync<ArgumentException>( It.IsAny<Func<Task>>(), It.IsAny<Action<(Exception Exception, int X, int Total)>>(), It.IsAny<int?>())) .Callback(async (Func<Task> action, Action<(Exception, int, int)> ontest, int? y) => { try { await action(); } catch (ArgumentException ex) { // 触发回调通知异常 ontest?.Invoke((ex, 2, 2)); // 如果需要让Mock的方法抛出异常,取消下面注释 // throw; } }) .Returns(Task.CompletedTask);
常见问题解决
- 泛型参数匹配失败:确保配置的泛型类型满足
where TException : Exception约束,或者使用It.IsAnyType(Moq 4.13+)适配所有合法泛型类型。 - 值元组参数识别错误:值元组作为委托参数时,直接用
It.IsAny<Action<(Exception, int, int)>>()即可,Moq能正确识别值元组类型。 - 异步返回值错误:异步方法必须返回有效
Task实例,不要返回null,用Task.CompletedTask表示无返回值的完成状态。
内容的提问来源于stack exchange,提问作者John Dotnet
相关产品推荐
相关产品推荐

