如何验证包含子Mock的Moq对象无其他调用?
Moq验证父Mock时意外触发子Mock未验证调用的问题
问题场景
以下测试代码执行后会失败:
[Test] public void Test() { var mockGetter = new Mock<IHostedServiceGetter>(); var mockService = new Mock<IHostedService>(); mockGetter.Setup(i => i.Get()).Returns(mockService.Object); mockGetter.Object.Get(); mockService.Object.StartAsync(CancellationToken.None); mockGetter.Verify(i => i.Get(), Times.Once); mockGetter.VerifyNoOtherCalls(); } public interface IHostedServiceGetter { public IHostedService Get(); }
抛出的异常信息:
Moq.MockException : Mock<IHostedService:1>: This mock failed verification due to the following unverified invocations: IHostedService.StartAsync(CancellationToken)
移除对mockService.Object.StartAsync()的调用后,测试即可通过。
对比另一个测试(仅返回新创建的Mock对象而非已定义的mockService),则能正常通过:
[Test] public void Test() { var mockGetter = new Mock<IHostedServiceGetter>(); var mockService = new Mock<IHostedService>(); mockGetter.Setup(i => i.Get()).Returns(new Mock<IHostedService>().Object); mockGetter.Object.Get(); mockService.Object.StartAsync(CancellationToken.None); mockGetter.Verify(i => i.Get(), Times.Once); mockGetter.VerifyNoOtherCalls(); }
编辑补充:将Setup改为延迟返回形式后,测试恢复正常:
mockGetter.Setup(i => i.Get()).Returns(() => mockService.Object);
原因解释
Moq内部有关联Mock跟踪机制:当直接把一个Mock对象(mockService.Object)作为另一个Mock的返回值时,Moq会自动为两个Mock建立关联。调用父Mock的VerifyNoOtherCalls()时,Moq会递归验证所有关联的子Mock是否存在未验证的调用,导致原本仅验证mockGetter的测试,意外触发了mockService的未验证调用检查。
而使用延迟返回(Returns(() => mockService.Object))时,Moq无法在Setup阶段提前捕获到返回的Mock对象,也就不会建立两者的关联,因此验证父Mock时只会检查自身调用,不会涉及子Mock。
第二个测试中返回的是全新的匿名Mock对象,和mockService无任何关联,所以mockService的调用不会影响mockGetter的验证。
解决方法
- 使用延迟返回的方式提供子Mock对象,即
Returns(() => mockService.Object),切断Moq对两个Mock的关联跟踪; - 如果不需要验证子Mock的调用,可以显式设置子Mock为宽松模式,比如创建时指定
new Mock<IHostedService>(MockBehavior.Loose),宽松模式下Moq默认不会校验未设置的调用; - 或者在验证父Mock前,手动清空子Mock的调用记录:
mockService.ResetCalls(),避免未验证调用被Moq检测到。
内容的提问来源于stack exchange,提问作者ewok
相关产品推荐
相关产品推荐

