Microsoft Fakes 中的代码生成、编译和命名约定
本主题讨论 Fakes 代码生成和编译中的选项和问题,并介绍了 Fakes 生成的类型、成员和参数的命名约定。
要求
- Visual Studio 旗舰版 •
主题内容
代码生成和编译
- 配置存根的代码生成 • 类型筛选 • 存根具体类和虚拟方法 • 内部类型 • 优化生成时段 • 避免程序集名称冲突
Fakes 命名约定
- 填充类型和存根类型命名约定 • 填充委托属性或存根委托字段命名约定 • 参数类型命名约定 • 递归规则
外部资源
- 指南
代码生成和编译
配置存根的代码生成
存根类型的生成在具有 .fakes 文件扩展名的 XML 文件中配置。Fakes 框架通过自定义 MSBuild 任务在生成进程中集成并在生成时检测这些文件。Fakes 代码生成器将存根类型编译为程序集并将引用添加到项目中。
下面的示例说明了 FileSystem.dll 中定义的存根类型:
<Fakes xmlns="https://schemas.microsoft.com/fakes/2011/">
<Assembly Name="FileSystem"/>
</Fakes>
类型筛选
可在 .fakes 文件中设置筛选器以限制应存根哪些类型。您可以在 StubGeneration 元素下添加数量不限的清除、添加、移除元素以生成所选类型的列表。
例如,此 .fakes 文件会在 System 和 System.IO 命名空间下生成类型的存根,但是排除系统中包含“Handle”的任何类型:
<Fakes xmlns="https://schemas.microsoft.com/fakes/2011/">
<Assembly Name="mscorlib" />
<!-- user code -->
<StubGeneration>
<Clear />
<Add Namespace="System!" />
<Add Namespace="System.IO!"/>
<Remove TypeName="Handle" />
</StubGeneration>
<!-- /user code -->
</Fakes>
筛选器字符串使用简单语法定义如何完成匹配:
默认情况下筛选器不区分大小写;筛选器可执行子字符串匹配:
el 匹配“hello”
添加 ! 到筛选器的末尾,使其成为精确区分大小写匹配:
el! 不匹配“您好”
hello! 匹配“hello”
添加 * 到筛选器的末尾,使其匹配字符串前缀:
el* 不匹配“您好”
he* 匹配“hello”
以分号分隔的列中的多个筛选器已合并为析取:
el;wo 匹配“hello”和“world”
将具体类和虚拟方法用作存根
默认情况下,为所有非密封类生成存根类型。可通过 .fakes 配置文件将存根类型限制为抽象类:
<Fakes xmlns="https://schemas.microsoft.com/fakes/2011/">
<Assembly Name="mscorlib" />
<!-- user code -->
<StubGeneration>
<Types>
<Clear />
<Add AbstractClasses="true"/>
</Types>
</StubGeneration>
<!-- /user code -->
</Fakes>
内部类型
Fakes 代码生成器将为对生成的 Fakes 程序集可见的类型生成填充码类型和存根类型。要使已填充的程序集的内部类型对 Fakes 和你的测试程序集可见,请将 InternalsVisibleToAttribute 特性添加到向生成的 Fakes 程序集和测试程序集提供可见性的已填充的程序集代码。这是一个示例:
// FileSystem\AssemblyInfo.cs
[assembly: InternalsVisibleTo("FileSystem.Fakes")]
[assembly: InternalsVisibleTo("FileSystem.Tests")]
强名称程序集中的内部类型
如果填充的程序集具有强名称,并且您希望访问程序集的内部类型:
测试程序集和 Fakes 程序集必须强命名。
必须将测试的公钥和 Fakes 程序集添加到已填充的程序集的 InternalsVisibleToAttribute 属性。这是当已填充的程序集为强名称时,在已填充的程序集代码中我们的示例属性所呈现的效果:
// FileSystem\AssemblyInfo.cs [assembly: InternalsVisibleTo("FileSystem.Fakes", PublicKey=<Fakes_assembly_public_key>)] [assembly: InternalsVisibleTo("FileSystem.Tests", PublicKey=<Test_assembly_public_key>)]
如果填充的程序集具有强名称,则 Fakes 框架将自动对生成的 Fakes 程序集进行强签名。您需要对测试程序集进行强签名。请参见 创建和使用具有强名称的程序集。
Fakes 框架使用相同的密钥为所有生成的程序集签名,因此,你可以将此代码片段用作起点以将 Fakes 程序集的“InternalsVisibleTo”特性添加到已填充的程序集代码。
[assembly: InternalsVisibleTo("FileSystem.Fakes, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e92decb949446f688ab9f6973436c535bf50acd1fd580495aae3f875aa4e4f663ca77908c63b7f0996977cb98fcfdb35e05aa2c842002703cad835473caac5ef14107e3a7fae01120a96558785f48319f66daabc862872b2c53f5ac11fa335c0165e202b4c011334c7bc8f4c4e570cf255190f4e3e2cbc9137ca57cb687947bc")]
通过指定 .snk 文件(包含作为 .fakes 文件的 Fakes\Compilation 元素中 KeyFile 属性值的替换密钥)的完整路径,您可以为 Fakes 程序集指定不同的公钥,例如您为填充的程序集创建的密钥。例如:
<-- FileSystem.Fakes.fakes -->
<Fakes ...>
<Compilation KeyFile="full_path_to_the_alternate_snk_file" />
</Fakes>
然后你需要使用备用 .snk 文件的公钥作为已填充的程序集代码的 Fakes 程序集的 InternalVisibleTo 属性的第二个参数:
// FileSystem\AssemblyInfo.cs
[assembly: InternalsVisibleTo("FileSystem.Fakes",
PublicKey=<Alternate_public_key>)]
[assembly: InternalsVisibleTo("FileSystem.Tests",
PublicKey=<Test_assembly_public_key>)]
在上面的示例中,Alternate_public_key 值和 Test_assembly_public_key 值可以相同。
优化生成时间
Fakes 程序集的编译可极大地增加生成时间。您通过为独立集中项目中的 .NET 系统程序集和第三方程序集生成 Fakes 程序集来最小化生成时间。由于您的计算机很少更改这种程序集,因此您可在其他项目中重新使用生成的 Fakes 程序集。
从单元测试项目,可以轻松引用放置在项目文件夹 FakesAssemblies 下编译的 Fakes 程序集。
使用与您的测试项目匹配的 .NET 运行时版本创建新的类库。我们称之为 Fakes.Prebuild。从项目中移除不需要的 class1.cs 文件。
添加引用到需要 Fakes 的所有系统和第三方程序集。
为每个程序集添加一个 .fakes 文件并生成。
从测试项目开始
确保具有对 Fakes 运行时 DLL 的引用:
C:\Program Files\Microsoft Visual Studio 12.0\Common7\IDE\PublicAssemblies\Microsoft.QualityTools.Testing.Fakes.dll
对于每个已为其创建 Fakes 的程序集,请添加引用到相应的 DLL 文件,该文件在项目文件夹 Fakes.Prebuild \ FakesAssemblies 中。
避免程序集名称冲突
在团队生成环境中,所有生成输出合并到一个目录中。如果出现多个使用 Fakes 的项目,可能发生不同版本的 Fakes 程序集相互覆盖的情况。例如,来自 .NET Framework 2.0 的 TestProject1 fakes mscorlib.dll 和 .NET Framework 4 的 TestProject2 fakes mscorlib.dll 都会产生一个 mscorlib.Fakes.dll Fakes 程序集。
要避免出现此问题,在添加 .fakes 文件时,Fakes 应自动为非项目引用创建版本限定的 Fakes 程序集名称。在创建 Fakes 程序集名称时,版本限定的 Fakes 程序集名称嵌入了版本号:
假定一个程序集 MyAssembly 和版本 1.2.3.4,则 Fakes 程序集名称为 MyAssembly.1.2.3.4.Fakes。
您可以通过在 .fakes: 中编辑程序集元素的版本特性来更改或移除此版本
attribute of the Assembly element in the .fakes:
<Fakes ...>
<Assembly Name="MyAssembly" Version="1.2.3.4" />
...
</Fakes>
Fakes 命名约定
填充类型和存根类型命名约定
命名空间
.Fakes 后缀添加到命名空间。
例如,System.Fakes 命名空间包含系统命名空间的填充码类型。
Global.Fakes 包含空命名空间的填充类型。
类型名称
将填充码前缀添加至类型名称可生成填充码类型名称。
例如,ShimExample 是示例类型的填充码类型。
存根前缀已添加到类型名称以生成存根类型名称。
例如,StubIExample 是 IExample 类型的存根类型。
类型参数和嵌套类型结构
泛型类型参数已复制。
嵌套类型结构以填充码类型进行复制。
填充委托属性或存根委托字段命名约定
字段命名的基本规则,从空的名称开始:
方法名称已追加。
如果方法名称是显式接口实现,则将移除点。
如果是泛型方法,则 Ofn 是追加的,而 n 是泛型方法参数的数字。
特殊方法名称(例如属性 getter 或 setter)将以下表所述方式处理。
如果方法是…… |
示例 |
附加方法名称 |
---|---|---|
构造函数 |
.ctor |
Constructor |
静态构造函数 |
.cctor |
StaticConstructor |
方法名称由以“_”分隔的两部分组成的访问器(例如属性 getter) |
kind_name(通常情况下,而不是由 ECMA 强制执行) |
NameKind,两部分均首字母大写且进行交换 |
属性 Prop 的 getter |
PropGet |
|
属性资源库 Prop |
PropSet |
|
事件添加程序 |
Add |
|
事件移除程序 |
Remove |
|
运算符由两部分组成 |
op_name |
NameOp |
例如:+ 运算符 |
op_Add |
AddOp |
对于转换运算符,追加返回类型。 |
T op_Implicit |
ImplicitOpT |
注释
索引器的 getter 和 setter 的处理方式类似于属性。索引器的默认名称为 Item。
参数类型名称已转换并串联。
除非具有重载多义性,否则将忽略返回类型。如果是这样,将在名称末尾追加返回类型
参数类型命名约定
假定 |
追加的字符串是… |
---|---|
一个类型T |
T 命名空间、嵌套结构和泛型 tic 已放置。 |
一个 out 参数out T |
TOut |
一个 ref 参数 ref T |
TRef |
一个数组类型T[] |
TArray |
一个多维数组类型 T[ , , ] |
T3 |
一种指针类型 T* |
TPtr |
一种泛型类型T<R1, …> |
TOfR1 |
类型 C<TType> 的泛型类型参数!i |
Ti |
方法 M<MMethod> 的泛型方法参数!!i |
Mi |
一种嵌套类型N.T |
N 被追加,然后 T |
递归规则
下面的规则按递归方式应用:
由于 Fakes 使用 C# 生成 Fakes 程序集,因此可生成无效 C# 标记的所有字符转义为“_”(下划线)。
如果一个结果名称与声明类型的任何成员发生冲突,则将通过追加一个两位数计数器(从 01 开始)使用编号架构。
外部资源
指南
Testing for Continuous Delivery with Visual Studio 2012 – Chapter 2: Unit Testing: Testing the Inside(使用 Visual Studio 2012 对连续交付进行测试 — 第 2 章:单元测试:测试内部)