NimbleMock:一个现代、极快的 .NET Mocking 库(比 Moq 快 34 倍)
Source: Dev.to

为什么我创建了 NimbleMock
多年来,我在企业 .NET 应用中遇到了常见的 Mock 痛点:
- 冗长的设置,需要为大型接口(为测试一个功能而 mock 20+ 方法)。
- 性能损耗,来自运行时代理(Moq 中的 Castle.DynamicProxy)——在大型测试套件中创建和验证速度慢。
- 静态/密封类的 Mock 地狱——没有原生支持,需要付费工具或包装器。
- 异步和泛型问题——出现意外的 null Task 或笨拙的类型推断。
- 脆弱的测试——本地通过的 Mock 在生产环境中因不匹配真实 API 而失败。
Moq 经受了考验,NSubstitute 代码简洁易读,但两者都没有充分利用现代 .NET 的源码生成器实现零开销。
NimbleMock 通过编译时魔法解决这些问题:无需运行时代理,激进的内联以及栈分配。结果?创建 Mock 快 34 倍,验证快 3 倍,在典型场景下零分配。
关键特性
- 部分 Mock – 只 Mock 需要的部分;未 Mock 的方法会抛出
NotImplementedException以便及早发现。 - 原生静态/密封类 Mock – 直接 Mock
DateTime.Now、Environment等。 - 一流的异步支持 – 对
Task与ValueTask无缝兼容。 - 泛型类型推断 – 完全支持嵌套泛型(例如 EF Core 的
IQueryable)。 - 流畅、可链式的 API – 受 NSubstitute 与 Moq 最佳实践启发。
- 防“说谎”验证 – 可选的运行时校验,针对真实的预发布 API 捕获过时的 Mock 结构。
- 编译时分析器 – 提前捕获错误(例如忘记对异步方法使用
SetupAsync时给出警告)。
MIT 许可证,开源,欢迎贡献!
快速开始
通过 NuGet 安装
dotnet add package NimbleMock
基本示例
using NimbleMock;
public interface IUserRepository
{
User GetById(int id);
Task SaveAsync(User user);
}
var mock = Mock.Of()
.Setup(x => x.GetById(1), new User { Id = 1, Name = "Alice" })
.SetupAsync(x => x.SaveAsync(default!), true)
.Build();
var user = mock.Object.GetById(1); // Returns Alice
mock.Verify(x => x.GetById(1)).Once();
部分 Mock(完美适用于上帝接口)
var mock = Mock.Partial()
.Only(x => x.GetData(1), expectedData)
.Build();
// 未 Mock 的方法会抛出,以保证安全
静态 Mock
var staticMock = Mock.Static()
.Returns(d => d.Now, new DateTime(2025, 12, 25))
.Build();
staticMock.Verify(d => d.Now).Once();
泛型示例
public interface IRepository
{
IQueryable Query();
}
var mock = Mock.Of]
.Setup(x => x.Query(), users.AsQueryable())
.Build();
var results = mock.Object.Query().Where(u => u.IsActive).ToList();
性能基准
在 .NET 8(BenchmarkDotNet)上运行。
Mock 创建与设置
| 库 | 时间 (ns) | 分配的内存 | 相对于 Moq 的提升 |
|---|---|---|---|
| Moq | 48,812 | 10.37 KB | 基准 |
| NSubstitute | 9,937 | 12.36 KB | ~5× 更快 |
| NimbleMock | 1,415 | 3.45 KB | 34× 更快 |
方法执行开销
| 库 | 时间 (µs) | 相对于 Moq 的提升 |
|---|---|---|
| Moq | ~1.4 | 基准 |
| NSubstitute | ~1.6 | 更慢 |
| NimbleMock | ~0.6 | 2.3× 更快 |
验证
| 库 | 时间 (ns) | 分配的内存 | 相对于 Moq 的提升 |
|---|---|---|---|
| Moq | 1,795 | 2.12 KB | 基准 |
| NSubstitute | 2,163 | 2.82 KB | 更慢 |
| NimbleMock | 585 | 0.53 KB | 3× 更快 |
由源码生成器驱动——无运行时反射!
自行运行基准:
dotnet run --project tests/NimbleMock.Benchmarks --configuration Release
从 Moq/NSubstitute 迁移
从 Moq 迁移
// Moq
var mock = new Mock<IUserRepository>();
mock.Setup(x => x.GetById(1)).Returns(user);
// NimbleMock
var mock = Mock.Of()
.Setup(x => x.GetById(1), user)
.Build();
验证同样更具可读性,使用流式 API。完整迁移指南请查看仓库。
防“说谎”验证:避免“说谎的测试”
独特功能:将 Mock 与真实端点进行校验。
var result = await LieProofing.AssertMatchesReal("https://api.staging.example.com");
if (!result.IsValid)
{
// 记录不匹配项
}
在生产故障前捕获 API 漂移。
最后感想
NimbleMock 为 .NET 8/9+ 时代而生:快速、安全、让 TDD 充满乐趣。如果你厌倦了缓慢的测试套件或静态 Mock 的 hack,快来试试吧!
- GitHub:
- NuGet:
欢迎反馈——你在当前 Mock 库中遇到哪些痛点?是否会因为原生静态支持和这般速度而切换?
⭐️ 给仓库点星,如果你喜欢它,让我们一起让测试重新变得有趣! 🚀