C# Fakes框架使用方法 C#如何使用Microsoft Fakes进行隔离测试

来源:这里教程网 时间:2026-02-21 17:42:27 作者:

什么是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()
)——但这类需求本身已属反模式,长期维护成本极高。

相关推荐