如何在C#单元测试中为Mock同一方法配置不同入参的调用规则
问题原因
这是C#的循环变量闭包捕获特性导致的问题:for循环中声明的count变量在整个循环周期内是同一个实例,你在Setup的Lambda表达式里直接引用count时,所有配置实际捕获的都是同一个变量。循环结束后count的值为3,最终所有Setup匹配的参数都变成了abc_3,自然只有最后一次调用能匹配成功。
解决方案
方案1:循环内新增临时变量固定迭代值
只需要在循环内部新增临时变量存储当前迭代的count值,就能避免闭包捕获同一变量的问题,你的原有逻辑只需要改两行:
var printerMock = new Mock<IMyPrinter>(MockBehavior.Loose); for (var count = 0; count < 4; count++) { // 新增临时变量,每次迭代都会生成独立的实例 var currentCount = count; printerMock.Setup(_ => _.Print($"abc_{currentCount}")); } var underTest = new MyObject(); underTest.Printer = printerMock.Object; underTest.foo(); printerMock.VerifyAll();
方案2:直接验证调用,无需提前配置
如果你只需要验证Print的调用是否符合预期,不需要提前Setup模拟行为,直接用Verify做批量校验更简洁:
var printerMock = new Mock<IMyPrinter>(MockBehavior.Loose); var underTest = new MyObject { Printer = printerMock.Object }; underTest.foo(); // 逐个验证每个参数的调用次数为1 for (var count = 0; count < 4; count++) { var currentCount = count; printerMock.Verify(_ => _.Print($"abc_{currentCount}"), Times.Once); }
方案3:批量捕获所有参数统一断言
如果需要对所有传入的参数做批量校验,可以用Moq的Capture功能捕获所有调用参数,再一次性做断言:
var printerMock = new Mock<IMyPrinter>(MockBehavior.Loose); var receivedMessages = new List<string>(); // 捕获所有调用Print方法传入的参数 printerMock.Setup(_ => _.Print(Capture.In(receivedMessages))); var underTest = new MyObject { Printer = printerMock.Object }; underTest.foo(); // 断言捕获到的参数和预期完全一致 var expectedMessages = Enumerable.Range(0, 4).Select(c => $"abc_{c}"); Assert.Equal(expectedMessages, receivedMessages);
内容的提问来源于stack exchange,提问作者NirMH
相关产品推荐
相关产品推荐

