分享方式:


Microsoft Fakes 中的程式碼產生、編譯和命名慣例

本文討論產生與編譯 Fakes 程式碼的選項和問題,並且描述 Fakes 產生類型、成員和參數的命名慣例。

需求

程式碼產生和編譯

設定虛設常式的程式碼產生

虛設常式類型會在有 .fakes 副檔名的 XML 檔中設定產生。 Fakes 框架會在建置流程中透過自訂 MSBuild 工作整合,並在建置期間偵測這些檔案。 Fakes 程式碼產生器會編譯虛設常式類型至組件內,並加入專案參考。

下列範例說明在 FileSystem.dll 中定義的虛設常式類型:

<Fakes xmlns="http://schemas.microsoft.com/fakes/2011/">
    <Assembly Name="FileSystem"/>
</Fakes>

類型篩選

篩選條件可以在 .fakes 檔案中設定,以限制應該設定哪些虛設常式類型。 您可以在 StubGeneration 項目下加入 Clear、Add、Remove 項目的未繫結數目,已建置所選類型的清單。

例如,下列 .fakes 檔案會針對在 System 和 System.IO 命名空間下的類型產生虛設常式,但是排除 System 中任何包含 "Handle" 的類型:

<Fakes xmlns="http://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! 比對符合 "hello"

  • * 新增至篩選條件的結尾會讓它符合字串的前置詞:

    el* 不符合 "hello"

    he* 比對符合 "hello"

  • 以分號分隔之清單中的多個篩選條件會結合為分離:

    el;wo 比對符合 "hello" 和 "world"

設定具象類別及虛擬方法的虛設常式

預設會為所有非密封類別產生虛設常式類型。 透過 .fakes 組態檔可將虛設常式類型限制為抽象類別:

<Fakes xmlns="http://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")]

您可以針對 Fakes 組件指定不同的公用金鑰,例如您已針對填充組件建立的金鑰,方法是指定 .snk 檔案的完整路徑,其中包含替代金鑰做為 .fakes 檔案之 Fakes\Compilation 項目中的 KeyFile 屬性值。 例如:

<-- 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_keyTest_assembly_public_key 兩個值可以是相同的。

最佳化建置時間

編譯 Fakes 組件可能大幅增加建置時間。 您也可以藉由在不同的集中式專案中產生 .NET System 組件的 Fake 組件和協力廠商組件,以最小化建置時間。 因為這類組件在電腦上極少變動,您可以在其他專案中重複使用產生的 Fakes 組件。

從單元測試專案中,請新增已編譯 Fakes 組件的參考,這些組件是放置在專案資料夾中的 FakesAssemblies 底下。

  1. 建立 .NET 執行階段版本與測試專案相符的新類別庫, 並稱它為 Fakes.Prebuild。 從專案刪除不需要的 class1.cs 檔。

  2. 將參考加入您所需之 Fakes 的所有系統和協力廠商組件。

  3. 為每個組件和組建新增 .fakes 檔案。

  4. 從您的測試專案

    • 確定您有 Fakes 執行階段 DLL 的參考:

      %ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\PublicAssemblies\Microsoft.QualityTools.Testing.Fakes.dll

    • 對於您已建立 Fakes 的每個組件,新增專案之 Fakes.Prebuild\FakesAssemblies 資料夾中對應 DLL 檔案的參考。

避免組件名稱發生衝突

在 Team Build 環境中,所有組建輸出會合併到單一目錄。 如果多個專案使用 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 中 Assembly 項目的 Version 屬性來變更或移除這個版本:

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 屬性的 setter PropSet
事件 adder Add
事件 remover Remove
由兩個部分組成的運算子 op_name NameOp
例如:+ 運算子 op_Add AddOp
若是轉換運算子,會附加傳回類型。 T op_Implicit ImplicitOpT

注意

  • 索引子的 getter 和 setter 是以類似屬性的方式來處理。 索引子的預設名稱是 Item
  • 會轉換並串連參數類型名稱。
  • 除非有多載模稜兩可的情況,否則會忽略傳回型別。 如果有多載模稜兩可的情況,則傳回型別將會附加在名稱結尾。

參數類型命名慣例

假設 附加的字串是...
類型T T

命名空間、巢狀結構和泛型 tics 會被丟棄。
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 開始。

在持續整合中使用 Microsoft Fakes

Microsoft Fakes 組件產生

Microsoft Fakes 是 Visual Studio Enterprise 中的專屬功能。 因此,在建置專案時,產生 Fakes 組件需要使用 Visual Studio 建置工作

注意

替代策略涉及到直接檢查您的 Fakes 組件到持續整合 (CI) 系統,並使用 MSBuild 工作。 如果您選擇此方法,您必須確定在測試專案中包含所產生 Fakes 組件的組件參考,如下列程式碼片段所示:

<Project Sdk="Microsoft.NET.Sdk">
    <ItemGroup>
        <Reference Include="FakesAssemblies\System.Fakes.dll"/>
    </ItemGroup>
</Project>

此參考必須手動新增,特別是 SDK 樣式專案 (也就是 .NET Core、.NET 5+ 和 .NET Framework),因為這些專案現在會隱含地新增組件參考。 如果您決定使用此方法,請務必在父元件進行變更時,更新 Fakes 組件。