什么是Fakes,它现在还能用吗
Fakes 是 Microsoft 为 .NET Framework(仅限)提供的轻量级隔离测试框架,核心是生成 FakesAssemblies
,把对 System
、mscorlib
或第三方程序集的调用重定向到可自定义行为的委托。但它**仅支持 .NET Framework 4.0+,不支持 .NET Core / .NET 5+**。Visual Studio 2019 是最后一个提供完整 Fakes 设计器支持的版本;VS 2022 已完全移除 Fakes 项目模板和右键“Add Fakes Assembly”菜单。
如果你正在用 .NET 6+ 或跨平台开发,
Microsoft.Fakes不可用——不是配置问题,而是根本没实现。别浪费时间查“为什么 Fakes 不生成”或“如何在 SDK 风格项目中启用”,答案统一:不能。
在 .NET Framework 项目中启用 Fakes 的关键步骤
Fakes 不是 NuGet 包,也不靠 dotnet test
驱动,它依赖 VS IDE 的专有生成逻辑和特定项目结构:
必须使用传统 .csproj
(即非 SDK 风格),且目标框架为 net472
或更高(但 ≤ net48
)
测试项目需引用待测程序集(.dll),右键该引用 → “Add Fakes Assembly” → VS 自动生成 xxx.Fakes
子文件夹和 .fakes
配置文件
生成后,项目自动添加对 Microsoft.QualityTools.Testing.Fakes
的引用,并在编译时调用 fakes.exe
工具生成桩类型(如 ShimDateTime
、StubIList<t></t>
)
务必在测试方法上标注 [TestMethod]
且类标记 [TestClass]
;Fakes 上下文只在 MSTest v2(Microsoft.VisualStudio.TestTools.UnitTesting
)中有效
常见失败现象:
CS0246 未能找到类型“ShimDateTime”—— 多半是没成功生成 Fakes 程序集,或项目 SDK 风格被误改;检查
obj\Fakes目录是否存在生成的
.dll和
.pdb。
Shim 和 Stub 的区别与典型用法
Fakes 提供两类桩类型:Shim
(针对静态/非虚成员,如 DateTime.Now
、File.ReadAllText
)和 Stub
(针对接口或虚方法,需传入实现):
Shim
必须在 ShimsContext.Create()
作用域内使用,离开即失效;它修改的是 IL 调用点,线程局部生效
Stub
是运行时对象替换,无需上下文,但只能用于可被继承/实现的成员(如 IRepository
接口)
避免在 Shim
中捕获异常后吞掉——它会干扰原始调用链;应明确 ShimXXX.BehaveAsNotImplemented()
或抛出自定义异常
示例:拦截
DateTime.Now
using (ShimsContext.Create())
{
ShimDateTime.NowGet = () => new DateTime(2020, 1, 1);
Assert.AreEqual(new DateTime(2020, 1, 1), DateTime.Now);
}
注意:
ShimDateTime类型由 Fakes 自动生成,命名规则是
Shim + 原类型名;属性访问器需写成
NowGet(而非
get_Now)。
为什么现在更推荐 Moq + Microsoft.Extensions.DependencyInjection
Fakes 的强耦合性带来几个硬伤:fakes.exe
生成慢、调试符号难追踪、无法用于 CI(因依赖 VS 安装)、不支持 async 成员桩(ShimTask
无可靠行为)。而现代替代方案更直接:
用 Moq
模拟接口(如 IMemoryCache
、IHttpClientFactory
),配合 ServiceCollection
替换服务,覆盖 80%+ 场景
对 DateTime
等基础类型,封装为 IDateTimeProvider
接口再注入,比 Shim 更易测、更清晰
对 File
、Registry
等系统 IO,改用内存实现(如 InMemoryDatabase
、MemoryStream
)或抽象为服务
Fakes 的唯一不可替代场景只剩:必须在不改源码前提下,临时劫持某个 .NET Framework 内部静态方法(如
Assembly.GetExecutingAssembly())——但这类需求本身已属反模式,长期维护成本极高。
