Поделиться через


Настройте анализ покрытия кода

По умолчанию покрытие кода анализирует все сборки решения, загруженные во время модульных тестов. Рекомендуется использовать это поведение по умолчанию, так как оно работает хорошо большую часть времени. Дополнительные сведения см. в статье Использование покрытия кода для определения объема протестированного кода.

Чтобы исключить тестовый код из результатов покрытия кода и включить только код приложения, добавьте атрибут ExcludeFromCodeCoverageAttribute в тестовый класс.

Чтобы включить сборки, которые не являются частью решения, получите PDB-файлы для этих сборок и скопируйте их в ту же папку, что и файлы сборки .dll.

Файл параметров запуска

Файл параметров запуска — это файл конфигурации, используемый средствами модульного тестирования. Дополнительные параметры покрытия кода указываются в файле .runsettings.

Чтобы настроить покрытие кода, следуйте следующим шагам.

  1. Добавьте файл параметров запуска в решение. В обозревателе решенийв контекстном меню решения выберите Добавить>новый элементи выберите XML-файл. Сохраните файл с таким именем, как CodeCoverage.runsettings.

    Если вы не видите все шаблоны элементов, выберите показать все шаблоны, а затем выберите шаблон элемента.

  2. Добавьте содержимое из примера файла в конце этой статьи и настройте его в соответствии с вашими потребностями, как описано в следующих разделах.

  3. Выберите файл параметров запуска.

    Начиная с Visual Studio 2019 версии 16.4, можно автоматически выбрать файл параметров запуска в корневом каталоге проекта. В противном случае в меню "Тест " выберите Настроить параметры выполнения, а затем выберите Файл параметров выполнения для всего решения. Чтобы указать файл параметров запуска для выполнения тестов из командной строки, см. раздел Настройка модульных тестов.

    Когда вы выбираете Analyze Code Coverage, информация о конфигурации считывается из файла настроек выполнения.

    Совет

    Любые предыдущие результаты покрытия кода и цвет кода не скрываются автоматически при выполнении тестов или обновлении кода.

    Чтобы отключить и включить настраиваемые параметры, отключите или выберите файл в меню test.

    Чтобы выбрать файл настроек выполнения, в меню Тест выберите Выбрать файл настроек. Чтобы указать файл параметров запуска для выполнения тестов из командной строки, см. раздел Настройка модульных тестов.

    При выборе Анализ покрытия кода, данные конфигурации считываются из файла настроек запуска.

    Совет

    Любые предыдущие результаты покрытия кода и цвет кода не скрываются автоматически при выполнении тестов или обновлении кода.

    Чтобы отключить и включить настраиваемые параметры, выберите тест, настройка параметров запускаи отмените выбор или выберите имя файла.

Пути поиска символов

Для сборок требуются файлы символов (.pdb файлы). Для сборок, созданных решением, файлы символов обычно присутствуют вместе с двоичными файлами, а покрытие кода работает автоматически. В некоторых случаях может потребоваться включить ссылки на сборки в анализ покрытия кода. В таких случаях PDB-файлы могут не находиться рядом с двоичными файлами, но можно указать путь поиска символов в файле .runsettings.

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

Заметка

Разрешение символов может занять некоторое время, особенно при использовании удалённого файла с множеством сборок. Поэтому рекомендуется копировать PDB-файлы в то же локальное расположение, что и двоичные (.dll и .exe) файлы.

Включение или исключение сборок и членов

Можно включить или исключить сборки или определенные типы и члены из анализа покрытия кода. Если раздел Include пуст или опущен, то включаются все загруженные сборки, у которых есть связанные файлы 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 Соотносит сборки по атрибуту и компании .
PublicKeyToken Совпадает с подписанными сборками по маркеру открытого ключа.
Источник Сопоставляет элементы по имени пути исходного файла, в котором они определены.
Атрибут Соответствует элементам, имеющим указанный атрибут. Укажите полное имя атрибута, например <Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>.

Если исключить атрибут CompilerGeneratedAttribute, код, использующий языковые функции, такие как async, await, yield returnи автоматически реализованные свойства, исключены из анализа покрытия кода. Чтобы исключить действительно созданный код, исключите только атрибут GeneratedCodeAttribute.
Функция Соответствует процедурам, функциям или методам по полному имени, включая список параметров. Вы также можете сопоставить часть имени с помощью регулярного выражения.

Примеры:

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

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

Форматы покрытия кода

По умолчанию покрытие кода собирается и сохраняется в файле .coverage. Вы также можете собирать покрытие в других форматах, таких как xml и Cobertura. Различные форматы могут быть полезны в разных редакторах и конвейерах. Это можно включить в настройках выполнения, добавив <Format>Cobertura</Format> или <Format>Xml</Format> в раздел конфигурации DataCollector в вашем файле runsettings. Этот формат можно просмотреть в окне результатов покрытия кода в Visual Studio Enterprise.

Кроме того, можно указать различные форматы из командной строки, указав его в файле runsettings или указав его в параметре. Например, командная строка dotnet использует dotnet test --collect:"Code Coverage;Format=Cobertura". Для vstest используется vstest.console.exe /collect:"Code Coverage;Format=Cobertura". Параметр collect переопределит формат, указанный в настройках runsettings.

Статическое и динамическое встроенное инструментирование

В версии 17.2 Visual Studio 2022 мы добавили опцию статического анализа двоичных файлов (на диске). В предыдущих версиях мы поддерживали только динамическое инструментирование, которое часто не могло инструментировать методы. Статическое собственное инструментирование является более стабильным, и оно рекомендуется. Статическое встроенное инструментирование требует включения параметра компоновки /PROFILE для всех собственных проектов, для которых необходимо собирать данные о покрытии кода.

Вы также можете включить встроенное статическое инструментирование в файле runsettings, добавив <EnableStaticNativeInstrumentation>True</EnableStaticNativeInstrumentation> в тег <CodeCoverage>. Используйте этот метод для сценариев командной строки.

По умолчанию динамическое встроенное инструментирование всегда включено. Если включено статическое и динамическое инструментирование, Visual Studio пытается инструментировать ваш C++ код статически, но если это невозможно (например, если опция линковки /PROFILE не включена), используется динамическое инструментирование. Вы можете полностью отключить динамическое встроенное инструментирование в настройках выполнения, добавив <EnableDynamicNativeInstrumentation>False</EnableDynamicNativeInstrumentation> в <CodeCoverage>.

Если включено статическое нативное инструментирование, нативные бинарные файлы будут инструментированы и заменены на диске перед выполнением теста. Исходные двоичные файлы будут восстановлены после выполнения теста. Вы можете отключить восстановление оригинальных файлов в настройках запуска, добавив <EnableStaticNativeInstrumentationRestore>False</EnableStaticNativeInstrumentationRestore> под тегом <CodeCoverage>. Это может быть особенно полезно в сценариях CI.

Если включено статическое нативное инструментирование, Visual Studio будет проводить поиск и инструментирование всех нативных двоичных файлов в каталоге, где находится тестовый двоичный файл. Можно указать дополнительные каталоги, в которых должны выполняться поиск двоичных файлов. В следующем примере указывается, что все собственные двоичные файлы из C:\temp и его подкаталогов должны быть инструментированы, кроме файлов, заканчивающихся Fabrikam.Math.dll.

<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>
            <Format>coverage</Format>
<!--
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>