自訂程式碼涵蓋範圍分析

根據預設,程式碼涵蓋範圍會分析在單元測試期間載入的所有方案組件。 建議您使用此預設行為,因為大部分時間都可以運作良好。 如需詳細資訊,請參閱使用程式碼涵蓋範圍來決定所測試的程式碼數量

若要從程式碼涵蓋範圍結果中排除測試程式碼,並且只包括應用程式程式碼,請將 ExcludeFromCodeCoverageAttribute 屬性新增至測試類別。

若要包括不屬於您方案的組件,請取得這些組件的 .pdb 檔案,並將這些檔案複製到組件 .dll 檔案的相同資料夾。

回合設定檔

回合設定檔是單元測試工具所使用的組態檔。 .runsettings 檔案中會指定進階的程式碼涵蓋範圍設定。

若要自訂程式碼涵蓋範圍,請遵循下列步驟:

  1. 將回合設定檔新增至方案。 在 [方案總管] 中,於方案的捷徑功能表上,選擇 [新增]>[新增項目],然後選取 [XML 檔案]。 儲存檔案,其名稱的格式必須是 CodeCoverage.runsettings

    如果您沒有看到所有項目範本,請選擇 [顯示所有範本],然後選擇項目範本。

  2. 新增本文結尾處範例檔中的內容,然後遵循下列各節中的描述並根據您自己的需求進行自訂。

  3. 選取回合設定檔案。

    從 Visual Studio 2019 16.4 版開始,您可以在專案根目錄中自動偵測回合設定檔案。 否則,在 [測試] 功能表上,選擇 [設定回合設定],然後選擇 [選取解決方案範圍的 runsettings 檔案]。 若要指定從命令列執行測試的回合設定檔,請參閱設定單元測試

    當您選取 [分析程式碼涵蓋範圍] 時,從回合設定檔讀取組態資訊。

    提示

    當您執行測試或更新程式碼時,並不會自動隱藏任何之前的程式碼涵蓋範圍結果及程式碼著色。

    若要開啟和關閉自訂設定,請取消選取或選取 [測試] 功能表中的檔案。

    若要選取回合設定檔案,請在 [測試] 功能表上,選擇 [選取設定檔案]。 若要指定從命令列執行測試的回合設定檔,請參閱設定單元測試

    當您選取 [分析程式碼涵蓋範圍] 時,從回合設定檔讀取組態資訊。

    提示

    當您執行測試或更新程式碼時,並不會自動隱藏任何之前的程式碼涵蓋範圍結果及程式碼著色。

    若要開啟和關閉自訂設定,請選擇 [測試]、[設定回合設定],然後取消選取或選取檔案名稱。

符號搜尋路徑

程式碼涵蓋範圍需要組件的符號檔 (.pdb 檔案)。 在您的方案所建置的組件中,符號檔案通常會和二進位檔一起出現,而且程式碼涵蓋範圍會自動運作。 在某些情況下,您可以在程式碼涵蓋範圍分析中加入參考的組件。 在此類情況下,.pdb 檔案不可以和二進位檔同時出現,不過您可以在 .runsettings 檔案中指定符號搜尋路徑。

<SymbolSearchPaths>
      <Path>\\mybuildshare\builds\ProjectX</Path>
      <!--More paths if required-->
</SymbolSearchPaths>

注意

符號解析可能需要一些時間,特別是在使用具有許多組件的遠端檔案位置時。 因此,請考慮將 .pdb 檔案複製到二進位 (.dll 和 .exe) 檔案在本機中的位置。

包含或排除組件和成員

您可以從程式碼涵蓋範圍分析中包含或排除組件或特定類型和成員。 如果 [包含] 區段是空的或省略,則會包含載入並具有相關聯 PDB 檔案的所有組件。 如果組件或成員符合 [排除] 區段中的子句,則會從程式碼涵蓋範圍中排除該子句。 [排除] 區段的優先順序高於 [包含] 區段:如果組件同時列在 [包含] 和 [排除] 中,則不會包含在程式碼涵蓋範圍中。

例如,下列 XML 會藉由指定其名稱來排除單一組件:

<ModulePaths>
  <Exclude>
   <ModulePath>.*Fabrikam.Math.UnitTest.dll</ModulePath>
   <!-- Add more ModulePath nodes here. -->
  </Exclude>
</ModulePaths>

下列範例會指定只有單一組件應該包含在程式碼涵蓋範圍中:

<ModulePaths>
  <Include>
   <ModulePath>.*Fabrikam.Math.dll</ModulePath>
   <!-- Add more ModulePath nodes here. -->
  </Include>
</ModulePaths>

下表顯示可針對在程式碼涵蓋範圍中包含或排除來比對組件和成員的各種方式。

XML 元素 相符項目
ModulePath 依組件名稱或檔案路徑比對指定的組件。
CompanyName Company 屬性比對組件。
PublicKeyToken 依公開金鑰權杖比對已簽署的組件。
來源 依來源檔案路徑名稱的定義方式比對元素。
屬性 比對具有指定屬性的元素。 指定屬性的完整名稱,例如 <Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>

如果您排除 CompilerGeneratedAttribute 屬性,則會從程式碼涵蓋範圍分析中排除使用語言功能 (例如 asyncawaityield return) 和自動實作屬性的程式碼。 若要排除真正產生的程式碼,只要排除 GeneratedCodeAttribute 屬性即可。
函式 依完整名稱 (包括參數清單) 比對程序、函式或方法。 您也可以使用規則運算式來比對名稱的部分。

範例:

Fabrikam.Math.LocalMath.SquareRoot(double); (C#)

Fabrikam::Math::LocalMath::SquareRoot(double) (C++)

程式碼涵蓋範圍格式

根據預設,程式碼涵蓋範圍會收集並儲存在 .coverage 檔案中。 您也可以使用其他格式收集涵蓋範圍,包括 XML 和 Cobertura。 不同編輯器和管線的不同格式可能很有用。 您可以藉由在 runsettings 檔案中的 DataCollector 組態區段中新增 <Format>Cobertura</Format><Format>Xml</Format>,在 runsettings 中啟用這個項目。 您可以在 Visual Studio Enterprise 的程式碼涵蓋範圍結果視窗中檢視此格式。

您也可以在 runsettings 檔案中指定,或在參數中指定,以從命令列指定不同的格式。 例如,dotnet 命令列會使用 dotnet test --collect:"Code Coverage;Format=Cobertura"。 針對 vstest,請使用 vstest.console.exe /collect:"Code Coverage;Format=Cobertura"。 collect 參數會覆寫 runsettings 中指定的格式。

靜態和動態原生檢測

在 Visual Studio 2022 17.2 版中,我們新增了選項以靜態方式檢測原生二進位檔 (在磁碟上)。 在舊版中,我們只支援動態檢測,這通常無法檢測方法。 靜態原生檢測比較穩定,建議使用。 靜態原生檢測需要針對您需要程式碼涵蓋範圍集合的所有原生專案啟用 /PROFILE 連結選項。

您可以在 [工具] > [選項] > [環境] > [預覽功能] 中啟用預覽功能程式碼涵蓋範圍原生靜態檢測,以啟用原生靜態檢測。

您也可以藉由在 <CodeCoverage> 標籤底下新增 <EnableStaticNativeInstrumentation>True</EnableStaticNativeInstrumentation>,在 runsettings 中啟用原生靜態檢測。 在命令列案例中使用這個方法。

根據預設,一律會啟用動態原生檢測。 如果同時啟用靜態和動態檢測,Visual Studio 會嘗試以靜態方式檢測您的 C++ 程式碼,但是如果不可行 (例如,未啟用 /PROFILE 連結選項時),將會使用動態檢測。 您可以藉由在 <CodeCoverage> 底下新增 <EnableDynamicNativeInstrumentation>False</EnableDynamicNativeInstrumentation>,在 runsettings 中完全停用動態原生檢測。

啟用靜態原生檢測時,會在測試執行之前,在磁碟上檢測並取代原生二進位檔。 測試執行之後,將會還原原始二進位檔。 您可以藉由在 <CodeCoverage> 標籤底下新增 <EnableStaticNativeInstrumentationRestore>False</EnableStaticNativeInstrumentationRestore>,在 runsettings 中停用還原原始檔案。 這在 CI 案例中特別有用。

啟用靜態原生檢測時,Visual Studio 會在測試二進位檔所在的目錄中搜尋和檢測所有原生二進位檔。 您可以指定應該搜尋二進位檔的其他目錄。 下列範例會指定除了以 Fabrikam.Math.dll 結尾的檔案之外,應該檢測來自 C:\temp 及其子目錄的所有原生二進位檔。

<ModulePaths>
  <IncludeDirectories>
    <Directory Recursive="true">C:\temp</Directory>
  </IncludeDirectories>
  <Exclude>
    <ModulePath>.*Fabrikam.Math.dll</ModulePath>
  </Exclude>
</ModulePaths>

規則運算式

包含和排除節點使用與萬用字元不同的規則運算式。 所有相符項目皆不區分大小寫。 部份範例如下:

  • .* 會比對任何字元的字串

  • \. 會比對點 "."

  • \( \) 會比對括弧 "( )"

  • \\ 會比對檔案路徑分隔符號 "\"

  • ^ 會比對字串的開頭

  • $ 會比對字串的結尾

下列 XML 示範如何使用規則運算式來包含和排除特定組件:

<ModulePaths>
  <Include>
    <!-- Include all loaded .dll assemblies (but not .exe assemblies): -->
    <ModulePath>.*\.dll$</ModulePath>
  </Include>
  <Exclude>
    <!-- But exclude some assemblies: -->
    <ModulePath>.*\\Fabrikam\.MyTests1\.dll$</ModulePath>
    <!-- Exclude all file paths that contain "Temp": -->
    <ModulePath>.*Temp.*</ModulePath>
  </Exclude>
</ModulePaths>

下列 XML 示範如何使用規則運算式來包含和排除特定函式:

<Functions>
  <Include>
    <!-- Include methods in the Fabrikam namespace: -->
    <Function>^Fabrikam\..*</Function>
    <!-- Include all methods named EqualTo: -->
    <Function>.*\.EqualTo\(.*</Function>
  </Include>
  <Exclude>
    <!-- Exclude methods in a class or namespace named UnitTest: -->
    <Function>.*\.UnitTest\..*</Function>
  </Exclude>
</Functions>

警告

如果規則運算式出現錯誤 (例如未逸出或不成對的括弧),則不會執行程式碼涵蓋範圍分析。

如需規則運算式的詳細資訊,請參閱在 Visual Studio 中使用規則運算式

範例 .runsettings 檔案

複製此程式碼,並根據需求編輯。

<?xml version="1.0" encoding="utf-8"?>
<!-- File name extension must be .runsettings -->
<RunSettings>
  <DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
        <Configuration>
          <CodeCoverage>
<!--
Additional paths to search for .pdb (symbol) files. Symbols must be found for modules to be instrumented.
If .pdb files are in the same folder as the .dll or .exe files, they are automatically found. Otherwise, specify them here.
Note that searching for symbols increases code coverage runtime. So keep this small and local.
-->
<!--
            <SymbolSearchPaths>
                   <Path>C:\Users\username\source\repos\ProjectX</Path>
                   <Path>\\mybuildshare\builds\ProjectX</Path>
            </SymbolSearchPaths>
-->

<!--
About include/exclude lists:
Empty "Include" clauses imply all; empty "Exclude" clauses imply none.
Each element in the list is a regular expression (ECMAScript syntax). See /visualstudio/ide/using-regular-expressions-in-visual-studio.
An item must first match at least one entry in the include list to be included.
Included items must then not match any entries in the exclude list to remain included.
-->

            <!-- Match assembly file paths: -->
            <ModulePaths>
              <Include>
                <ModulePath>.*\.dll$</ModulePath>
                <ModulePath>.*\.exe$</ModulePath>
              </Include>
              <Exclude>
                <ModulePath>.*CPPUnitTestFramework.*</ModulePath>
              </Exclude>
              <!-- Specifies additional list of directories where binaries static native instrumentation should be searched. -->
              <IncludeDirectories>
                <Directory Recursive="true">C:\b59fb11c-1611-4562-9a2b-c35719da65d3</Directory>
              </IncludeDirectories>
            </ModulePaths>

            <!-- Match fully qualified names of functions: -->
            <!-- (Use "\." to delimit namespaces in C# or Visual Basic, "::" in C++.)  -->
            <Functions>
              <Exclude>
                <Function>^Fabrikam\.UnitTest\..*</Function>
                <Function>^std::.*</Function>
                <Function>^ATL::.*</Function>
                <Function>.*::__GetTestMethodInfo.*</Function>
                <Function>^Microsoft::VisualStudio::CppCodeCoverageFramework::.*</Function>
                <Function>^Microsoft::VisualStudio::CppUnitTestFramework::.*</Function>
              </Exclude>
            </Functions>

            <!-- Match attributes on any code element: -->
            <Attributes>
              <Exclude>
                <!-- Don't forget "Attribute" at the end of the name -->
                <Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>
                <Attribute>^System\.Diagnostics\.DebuggerNonUserCodeAttribute$</Attribute>
                <Attribute>^System\.CodeDom\.Compiler\.GeneratedCodeAttribute$</Attribute>
                <Attribute>^System\.Diagnostics\.CodeAnalysis\.ExcludeFromCodeCoverageAttribute$</Attribute>
              </Exclude>
            </Attributes>

            <!-- Match the path of the source files in which each method is defined: -->
            <Sources>
              <Exclude>
                <Source>.*\\atlmfc\\.*</Source>
                <Source>.*\\vctools\\.*</Source>
                <Source>.*\\public\\sdk\\.*</Source>
                <Source>.*\\microsoft sdks\\.*</Source>
                <Source>.*\\vc\\include\\.*</Source>
              </Exclude>
            </Sources>

            <!-- Match the company name property in the assembly: -->
            <CompanyNames>
              <Exclude>
                <CompanyName>.*microsoft.*</CompanyName>
              </Exclude>
            </CompanyNames>

            <!-- Match the public key token of a signed assembly: -->
            <PublicKeyTokens>
              <!-- Exclude Visual Studio extensions: -->
              <Exclude>
                <PublicKeyToken>^B77A5C561934E089$</PublicKeyToken>
                <PublicKeyToken>^B03F5F7F11D50A3A$</PublicKeyToken>
                <PublicKeyToken>^31BF3856AD364E35$</PublicKeyToken>
                <PublicKeyToken>^89845DCD8080CC91$</PublicKeyToken>
                <PublicKeyToken>^71E9BCE111E9429C$</PublicKeyToken>
                <PublicKeyToken>^8F50407C4E9E73B6$</PublicKeyToken>
                <PublicKeyToken>^E361AF139669C375$</PublicKeyToken>
              </Exclude>
            </PublicKeyTokens>

            <!-- We recommend you do not change the following values: -->

            <!-- Set this to True to collect coverage information for functions marked with the "SecuritySafeCritical" attribute. Instead of writing directly into a memory location from such functions, code coverage inserts a probe that redirects to another function, which in turns writes into memory. -->
            <UseVerifiableInstrumentation>True</UseVerifiableInstrumentation>
            <!-- When set to True, collects coverage information from child processes that are launched with low-level ACLs, for example, UWP apps. -->
            <AllowLowIntegrityProcesses>True</AllowLowIntegrityProcesses>
            <!-- When set to True, collects coverage information from child processes that are launched by test or production code. -->
            <CollectFromChildProcesses>True</CollectFromChildProcesses>
            <!-- When set to True, restarts the IIS process and collects coverage information from it. -->
            <CollectAspDotNet>False</CollectAspDotNet>
            <!-- When set to True, static native instrumentation will be enabled. -->
            <EnableStaticNativeInstrumentation>True</EnableStaticNativeInstrumentation>
            <!-- When set to True, dynamic native instrumentation will be enabled. -->
            <EnableDynamicNativeInstrumentation>True</EnableDynamicNativeInstrumentation>
            <!-- When set to True, instrumented binaries on disk are removed and original files are restored. -->
            <EnableStaticNativeInstrumentationRestore>True</EnableStaticNativeInstrumentationRestore>

          </CodeCoverage>
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>
</RunSettings>
<?xml version="1.0" encoding="utf-8"?>
<!-- File name extension must be .runsettings -->
<RunSettings>
  <DataCollectionRunSettings>
    <DataCollectors>
      <DataCollector friendlyName="Code Coverage" uri="datacollector://Microsoft/CodeCoverage/2.0" assemblyQualifiedName="Microsoft.VisualStudio.Coverage.DynamicCoverageDataCollector, Microsoft.VisualStudio.TraceCollector, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
        <Configuration>
          <CodeCoverage>
<!--
Additional paths to search for .pdb (symbol) files. Symbols must be found for modules to be instrumented.
If .pdb files are in the same folder as the .dll or .exe files, they are automatically found. Otherwise, specify them here.
Note that searching for symbols increases code coverage runtime. So keep this small and local.
-->
<!--
            <SymbolSearchPaths>
                   <Path>C:\Users\username\source\repos\ProjectX</Path>
                   <Path>\\mybuildshare\builds\ProjectX</Path>
            </SymbolSearchPaths>
-->

<!--
About include/exclude lists:
Empty "Include" clauses imply all; empty "Exclude" clauses imply none.
Each element in the list is a regular expression (ECMAScript syntax). See /visualstudio/ide/using-regular-expressions-in-visual-studio.
An item must first match at least one entry in the include list to be included.
Included items must then not match any entries in the exclude list to remain included.
-->

            <!-- Match assembly file paths: -->
            <ModulePaths>
              <Include>
                <ModulePath>.*\.dll$</ModulePath>
                <ModulePath>.*\.exe$</ModulePath>
              </Include>
              <Exclude>
                <ModulePath>.*CPPUnitTestFramework.*</ModulePath>
              </Exclude>
              <!-- Specifies additional list of directories where binaries static native instrumentation should be searched. -->
              <IncludeDirectories>
                <Directory Recursive="true">C:\b59fb11c-1611-4562-9a2b-c35719da65d3</Directory>
              </IncludeDirectories>
            </ModulePaths>

            <!-- Match fully qualified names of functions: -->
            <!-- (Use "\." to delimit namespaces in C# or Visual Basic, "::" in C++.)  -->
            <Functions>
              <Exclude>
                <Function>^Fabrikam\.UnitTest\..*</Function>
                <Function>^std::.*</Function>
                <Function>^ATL::.*</Function>
                <Function>.*::__GetTestMethodInfo.*</Function>
                <Function>^Microsoft::VisualStudio::CppCodeCoverageFramework::.*</Function>
                <Function>^Microsoft::VisualStudio::CppUnitTestFramework::.*</Function>
              </Exclude>
            </Functions>

            <!-- Match attributes on any code element: -->
            <Attributes>
              <Exclude>
                <!-- Don't forget "Attribute" at the end of the name -->
                <Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>
                <Attribute>^System\.Diagnostics\.DebuggerNonUserCodeAttribute$</Attribute>
                <Attribute>^System\.CodeDom\.Compiler\.GeneratedCodeAttribute$</Attribute>
                <Attribute>^System\.Diagnostics\.CodeAnalysis\.ExcludeFromCodeCoverageAttribute$</Attribute>
              </Exclude>
            </Attributes>

            <!-- Match the path of the source files in which each method is defined: -->
            <Sources>
              <Exclude>
                <Source>.*\\atlmfc\\.*</Source>
                <Source>.*\\vctools\\.*</Source>
                <Source>.*\\public\\sdk\\.*</Source>
                <Source>.*\\microsoft sdks\\.*</Source>
                <Source>.*\\vc\\include\\.*</Source>
              </Exclude>
            </Sources>

            <!-- Match the company name property in the assembly: -->
            <CompanyNames>
              <Exclude>
                <CompanyName>.*microsoft.*</CompanyName>
              </Exclude>
            </CompanyNames>

            <!-- Match the public key token of a signed assembly: -->
            <PublicKeyTokens>
              <!-- Exclude Visual Studio extensions: -->
              <Exclude>
                <PublicKeyToken>^B77A5C561934E089$</PublicKeyToken>
                <PublicKeyToken>^B03F5F7F11D50A3A$</PublicKeyToken>
                <PublicKeyToken>^31BF3856AD364E35$</PublicKeyToken>
                <PublicKeyToken>^89845DCD8080CC91$</PublicKeyToken>
                <PublicKeyToken>^71E9BCE111E9429C$</PublicKeyToken>
                <PublicKeyToken>^8F50407C4E9E73B6$</PublicKeyToken>
                <PublicKeyToken>^E361AF139669C375$</PublicKeyToken>
              </Exclude>
            </PublicKeyTokens>

            <!-- We recommend you do not change the following values: -->

            <!-- Set this to True to collect coverage information for functions marked with the "SecuritySafeCritical" attribute. Instead of writing directly into a memory location from such functions, code coverage inserts a probe that redirects to another function, which in turns writes into memory. -->
            <UseVerifiableInstrumentation>True</UseVerifiableInstrumentation>
            <!-- When set to True, collects coverage information from child processes that are launched with low-level ACLs, for example, UWP apps. -->
            <AllowLowIntegrityProcesses>True</AllowLowIntegrityProcesses>
            <!-- When set to True, collects coverage information from child processes that are launched by test or production code. -->
            <CollectFromChildProcesses>True</CollectFromChildProcesses>
            <!-- When set to True, restarts the IIS process and collects coverage information from it. -->
            <CollectAspDotNet>False</CollectAspDotNet>

          </CodeCoverage>
        </Configuration>
      </DataCollector>
    </DataCollectors>
  </DataCollectionRunSettings>
</RunSettings>