什么是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<IEmailSender>();<br>
mockSender.Setup(x => x.Send("test@example.com", "Welcome!"))<br>
.Returns("OK");<br>
var service = new NotificationService(mockSender.Object);<br>
var result = service.Notify("test@example.com");<br>
Assert.Equal("OK", result);
关键点:
-
Mock<t>()</t>创建模拟器
-
.Setup()定义调用规则(参数匹配很重要)
-
.Object获取模拟实例供被测类使用
进阶技巧:验证调用次数与参数约束
除了返回值,你还可以检查方法是否被调用、调用了几次、参数是否符合预期:
mock.Setup(x => x.Send(It.IsAny<string>(), It.IsAny<string>())).Returns("OK");</string></string> —— 忽略具体参数值
mock.Verify(x => x.Send("a@b.com", "Welcome!"), Times.Once()); —— 验证精确调用一次
mock.Verify(x => x.Send(It.Is<string>(s => s.Contains("@"))), Times.AtLeastOnce());</string> —— 自定义参数校验
mock.Setup(x => x.Send(It.IsAny<string>(), It.IsAny<string>())).Throws<invalidoperationexception>();</invalidoperationexception></string></string>—— 模拟异常场景
注意事项和常见坑
Moq 不是万能的:
- 不能 mock
static、
sealed类或非虚(non-virtual)方法
- 属性默认是只读的,要 mock set 访问器需显式声明为
virtual set
- 使用
It.Is<t>()</t>时注意闭包捕获变量,避免测试不稳定
- 过度 mock 可能掩盖设计问题(比如依赖太多、职责不清),优先考虑重构而非硬 mock
基本上就这些。Moq 上手快,但真正写好模拟测试,核心还是理解“隔离”和“行为驱动”的思路。
