Moq调用参数后续调用变更问题:验证结果不符合预期
问题:Moq验证参数时因引用类型导致调用次数不符
Processor类会两次调用IService的DoSomething方法,传入同一个InputParameters对象:先设置x=5,修改x=100后再次调用。单元测试期望验证DoSomething分别以x=5和x=100各被调用一次,但实际验证显示两次调用的x值均为100。调试发现第一次调用的参数在修改x=100时被改变,疑惑Moq为何仅保存参数引用,且尝试不同Setup配置后结果一致。
代码示例
InputParameters类
public class InputParameters { public int x, y; }
IService接口与Service实现
public interface IService { bool DoSomething(InputParameters input); } public class Service : IService { public bool DoSomething(InputParameters input) { return input.x > input.y; } }
Processor类
public class Processor { public IService _service; public Processor(IService theServive) { _service = theServive; } public void Process() { InputParameters input = new InputParameters(); input.x = 5; input.y = 50; _service.DoSomething(input); input.x = 100; _service.DoSomething(input); } }
单元测试代码
private Mock<IService> _serviceMock; private Processor _processor; [SetUp] public void Setup() { _serviceMock = new Mock<IService>(); _processor = new Processor(_serviceMock.Object); } [Test] public void Test() { _serviceMock.Setup(service => service.DoSomething( It.IsAny<InputParameters>() )).Returns(() => false); _processor.Process(); _serviceMock.Verify(service => service.DoSomething( It.Is<InputParameters>(input => input.x == 5) ), Times.Exactly(1)); // 返回0次调用 _serviceMock.Verify(service => service.DoSomething( It.Is<InputParameters>(input => input.x == 100) ), Times.Exactly(1)); // 返回2次调用 }
尝试过的其他Setup配置
_serviceMock.Setup(service => service.DoSomething( It.Is<InputParameters>(input => input.x == 5) )).Returns(() => true); _serviceMock.Setup(service => service.DoSomething( It.Is<InputParameters>(input => input.x == 100) )).Returns(() => false);
原因与解决方案
问题原因
这是因为InputParameters是引用类型,Moq在记录方法调用时,保存的是对象的引用而非调用时刻的参数快照。当你在第一次调用后修改对象的x值,Moq执行验证逻辑时访问的是同一个对象的当前状态,因此两次验证都会匹配x=100。
解决方法
方法1:每次调用创建新的参数对象
避免复用同一个引用对象,每次调用都实例化新的InputParameters:
public void Process() { // 第一次调用用新对象 var input1 = new InputParameters { x = 5, y = 50 }; _service.DoSomething(input1); // 第二次调用用另一个新对象 var input2 = new InputParameters { x = 100, y = 50 }; _service.DoSomething(input2); }
方法2:捕获调用时的参数快照
如果必须复用对象,可以在Moq中通过Callback捕获每次调用时的参数状态,直接记录当时的x值:
[Test] public void Test() { var capturedXValues = new List<int>(); _serviceMock.Setup(s => s.DoSomething(It.IsAny<InputParameters>())) // 每次调用时记录当前x值 .Callback<InputParameters>(input => capturedXValues.Add(input.x)) .Returns(false); _processor.Process(); // 验证捕获的x值序列是否符合预期 Assert.That(capturedXValues, Is.EqualTo(new List<int> { 5, 100 })); }
内容的提问来源于stack exchange,提问作者gyulak
相关产品推荐
相关产品推荐

