Moq 是什么,什么时候该用它
Moq 是 C# 中最主流的 mocking 框架,专为单元测试设计,用来替代真实依赖(比如
IDataService、
IRepository<t></t>)——不是为了“绕过逻辑”,而是为了**隔离被测代码,控制输入与行为,验证交互是否符合预期**。它只在测试项目中使用,生产代码里不该出现
Mock<t></t>。
安装 Moq 并创建基础 Mock 对象
先通过 NuGet 安装:
Moq包(当前稳定版支持 .NET 5+ 和 .NET Standard 2.0+)。安装后,用
Mock<t></t>构造器创建模拟实例:
var mockLogger = new Mock<ILogger>();
注意:
T必须是接口或带
virtual成员的类(Moq 无法 mock sealed 类或非 virtual 方法)。如果你 mock 一个类,且该类有无参构造函数,Moq 会调用它;否则需显式传入构造参数。 接口 mock 最安全,推荐优先使用 mock 类时,所有非 virtual 成员仍走原实现,容易误判行为 别对
static或
internal成员设期望——Moq 无法拦截它们
设置方法返回值和行为(Setup / Returns)
用
Setup()声明某个方法被调用时的响应。最常见的是
Returns():
mockLogger.Setup(x => x.Log("Error occurred")).Returns(true);但更实用的是按参数匹配或返回动态值:
用It.IsAny<string>()</string>匹配任意字符串参数:
Setup(x => x.Log(It.IsAny<string>())).Returns(true)</string>用
Returns((string msg) => $"[LOG]{msg}") 实现委托返回,可做简单转换
用 Throws<argumentexception>()</argumentexception>模拟异常场景
SetupSequence()可定义多次调用的不同返回值(如第一次成功、第二次失败)
⚠️ 常见错误:写成
Setup(x => x.Log("Error occurred")).Returns(true),但实际调用是 Log("error occurred")(大小写不同)——匹配失败,返回默认值(false或
null),测试看似通过实则没覆盖逻辑。
验证方法是否被调用(Verify)
仅设期望不够,还要确认被测代码确实按约定调用了依赖。用
Verify()断言:
mockLogger.Verify(x => x.Log(It.IsAny<string>()), Times.Once());
关键点:
Times.Once()、
Times.AtLeastOnce()、
Times.Exactly(2)等控制调用频次 不带参数的
Verify()会检查所有已
Setup的成员是否被调用(慎用,易导致过度断言) 如果方法被调用但参数不匹配(比如期望
"User not found",实际传了
"user not found"),
Verify会失败并抛出
Moq.MockException异步方法要 mock
Task返回值,并用
Verify()配合
Times,不能只靠 await 后断言结果
真正难的不是写 Setup,而是想清楚:这个依赖在当前测试场景下,应该被调用几次?传什么参数?有没有边界条件(null、空字符串、超长文本)需要覆盖?这些决定了 Setup 和 Verify 的粒度。
