Moq 是一个轻量易用的 .NET 模拟框架,用于通过接口和虚方法创建假对象以隔离被测代码,提升单元测试的速度、可重复性与可靠性。

什么是Moq,为什么用它做单元测试
Moq 是一个轻量、易用的 .NET 模拟(Mock)框架,专为接口和虚方法设计。它让你在不依赖真实实现的情况下,创建“假对象”来隔离被测代码——比如模拟数据库访问、HTTP 客户端或第三方服务。这样写单元测试时,速度快、可重复、不污染数据,也更容易验证行为逻辑是否正确。
安装 Moq 并准备基础环境
在项目中通过 NuGet 安装 Moq 包:
dotnet add package Moq(.NET CLI)
或在 Visual Studio 中右键项目 → “管理 NuGet 包” → 搜索 Moq → 安装最新稳定版。
确保你的待测类依赖的是接口(如 IService),而不是具体类型。Moq 无法直接 mock 非虚成员或密封类。
简单示例:mock 接口并设置返回值
假设你有如下接口和待测服务:
// 接口定义public interface IEmailSender { string Send(string to, string body); }
// 被测类public class NotificationService { private readonly IEmailSender _sender; public NotificationService(IEmailSender sender) => _sender = sender; public string Notify(string user) => _sender.Send(user, "Welcome!"); }
用 Moq 编写测试:
var mockSender = new Mock();
mockSender.Setup(x => x.Send("test@example.com", "Welcome!"))
.Returns("OK");
var service = new NotificationService(mockSender.Object);
var result = service.Notify("test@example.com");
Assert.Equal("OK", result);
关键点:
- Mock 创建模拟器
- .Setup() 定义调用规则(参数匹配很重要)
- .Object 获取模拟实例供被测类使用
进阶技巧:验证调用次数与参数约束
除了返回值,你还可以检查方法是否被调用、调用了几次、参数是否符合预期:
-
mock.Setup(x => x.Send(It.IsAny—— 忽略具体参数值(), It.IsAny ())).Returns("OK"); -
mock.Verify(x => x.Send("a@b.com", "Welcome!"), Times.Once());—— 验证精确调用一次 -
mock.Verify(x => x.Send(It.Is—— 自定义参数校验(s => s.Contains("@"))), Times.AtLeastOnce()); -
mock.Setup(x => x.Send(It.IsAny—— 模拟异常场景(), It.IsAny ())).Throws ();
注意事项和常见坑
Moq 不是万能的:
- 不能 mock static、sealed 类或非虚(non-virtual)方法
- 属性默认是只读的,要 mock set 访问器需显式声明为 virtual set
- 使用 It.Is 时注意闭包捕获变量,避免测试不稳定
- 过度 mock 可能掩盖设计问题(比如依赖太多、职责不清),优先考虑重构而非硬 mock
基本上就这些。Moq 上手快,但真正写好模拟测试,核心还是理解“隔离”和“行为驱动”的思路。









