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


Настройка анализа объема протестированного кода

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

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

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

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

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

Чтобы настроить объем протестированного кода, выполните следующие действия:

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

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

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

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

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

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

    Совет

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

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

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

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

    Совет

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

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

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

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

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

Примечание.

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

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

Вы можете включать сборки или определенные типы и члены в анализ объема протестированного кода или исключать их из него. Если раздел Include пуст или отсутствует, включаются все загруженные сборки, с которыми связаны PDB-файлы. Если сборка или член соответствует предложению в разделе Exclude, то они исключаются из анализа объема протестированного кода. Раздел Exclude имеет приоритет над разделом Include: если сборка указана как в разделе Include, так и в разделе Exclude, она не включается в анализ объема протестированного кода.

Например, в следующем коде 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, код, использующий языковые функции, такие как async, await, yield return, и автоматические реализуемые свойства, исключается из анализа объема протестированного кода. Чтобы исключить созданный код, исключите только атрибут GeneratedCodeAttribute.
Function Сопоставление процедур, функций или методов по полному имени, включая список параметров. Возможно также сопоставление с частью имени с помощью регулярного выражения.

Примеры:

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.

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

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

Вы можете включить собственное статическое инструментирование, включив предварительную версию функции Собственное статическое инструментирование протестированного кода в разделе Инструменты > Параметры > Среда > Предварительные версии функций.

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

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

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

Если включено статическое собственное инструментирование, 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>

Предупреждение

Если в регулярном выражении есть ошибка, например круглые скобки без escape-последовательности или непарные круглые скобки, то анализ объема протестированного кода не выполняется.

Дополнительные сведения о регулярных выражениях см. в статье Использование регулярных выражений в 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>