含可选参数的Mock方法调用方式不同返回结果异常不一致
问题:Moq模拟带可选参数的接口时,不同调用方式返回结果不一致
我Mock了一个带有可选参数的接口方法,预期它返回true,但当仅传入必填参数(使用默认可选参数)和传入所有参数时,得到的结果却不同。以下是完整可复现的示例代码:
using Moq; using System.Threading; using System.Threading.Tasks; using Xunit.Abstractions; namespace MoqTest { public interface IProcedureService { Task<bool> InterfaceMethod(string req1, int req2, int? opt1 = null, CancellationToken opt2 = default); } public class Tests { Mock<IProcedureService> _procedureService = new Mock<IProcedureService>(); ITestOutputHelper _output; public Tests(ITestOutputHelper output) => _output = output; [Xunit.Fact] public async void DoTheTest() { _procedureService.Setup(x => x.InterfaceMethod(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<CancellationToken>())) .ReturnsAsync(true); _output.WriteLine($"result from IsUniqueProcedureNameInWorkspace (using optional defaults) " + $"{(await _procedureService.Object.InterfaceMethod("", 0))}"); // returns false _output.WriteLine($"result from IsUniqueProcedureNameInWorkspace (NOT using optional defaults) " + $"{(await _procedureService.Object.InterfaceMethod("", 0, 0, default))}"); // returns true } } }
执行后输出如下:
Standard Output: result from IsUniqueProcedureNameInWorkspace (using optional defaults) False result from IsUniqueProcedureNameInWorkspace (NOT using optional defaults) True
我原本预期两种调用方式都返回True,因为我在Setup中使用It.IsAny匹配了所有参数。请问我忽略了什么?我的Moq版本为4.20.70。
原因与解决方案
核心原因
Moq的方法匹配严格遵循参数类型匹配规则:你在Setup中用It.IsAny<int>()匹配第三个参数,但接口定义里的opt1是int?(可空int)类型。int和int?是不同的类型,It.IsAny<int>()无法匹配int?类型的参数(即使该参数的值是null)。
当你调用InterfaceMethod("", 0)时,第三个参数传递的是默认值null(int?类型),无法命中当前Setup,所以Moq返回该方法的默认值false;而调用InterfaceMethod("", 0, 0, default)时,第三个参数是int类型的0,会被隐式转换为int?,此时Setup里的It.IsAny<int>()刚好匹配这个隐式转换后的基础类型值,因此命中Setup返回true。
修复方案
将Setup中的It.IsAny<int>()替换为It.IsAny<int?>(),确保参数类型完全匹配:
_procedureService.Setup(x => x.InterfaceMethod(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int?>(), It.IsAny<CancellationToken>())) .ReturnsAsync(true);
修改后,两种调用方式都会命中该Setup:
- 省略可选参数时,
opt1是null(int?类型),It.IsAny<int?>()可以匹配; - 传入
0作为opt1时,0会被隐式转换为int?,同样能被It.IsAny<int?>()匹配。
这样两种调用都会返回预期的true。
内容的提问来源于stack exchange,提问作者Stormenet
相关产品推荐
相关产品推荐

