Personnaliser l’analyse de la couverture du code

Par défaut, la couverture du code analyse tous les assemblys de la solution chargés pendant les tests unitaires. Nous vous recommandons d’utiliser ce comportement par défaut, car il est généralement efficace. Pour plus d’informations, consultez Utiliser la couverture du code pour déterminer la quantité de code testé.

Pour exclure le code de test des résultats de la couverture du code et inclure uniquement le code d’application, ajoutez l’attribut ExcludeFromCodeCoverageAttribute à votre classe de test.

Pour inclure des assemblys qui ne font pas partie de votre solution, obtenez les fichiers .pdb de ces assemblys et copiez-les dans le même dossier que les fichiers .dll de l’assembly.

Fichier de paramètres d’exécution

Le fichier de paramètres d’exécution est le fichier de configuration utilisé par les outils de test unitaire. Les paramètres avancés de la couverture du code sont spécifiées dans un fichier .runsettings.

Pour personnaliser la couverture du code, effectuez les étapes suivantes :

  1. Ajoutez un fichier de paramètres d’exécution à votre solution. Dans l’Explorateur de solutions, dans le menu contextuel de votre solution, choisissez Ajouter>Nouvel élément, puis sélectionnez Fichier XML. Enregistrez le fichier sous un nom comme CodeCoverage.runsettings.

    Si vous ne voyez pas tous les modèles d’élément, choisissez Afficher tous les modèles, puis choisissez le modèle d’élément.

  2. Ajoutez le contenu de l’exemple de fichier à la fin de cet article, puis personnalisez-le selon vos besoins comme décrit dans les sections qui suivent.

  3. Sélectionnez un fichier de paramètre d’exécution.

    À partir de Visual Studio 2019 version 16.4, vous pouvez détecter automatiquement un fichier de paramètres d’exécution à la racine du projet. Sinon, dans le menu Tester, choisissez Configurer les paramètres d’exécution, puis choisissez Sélectionnez le fichier d’exécution à l’échelle de la solution. Pour spécifier un fichier de paramètres d’exécution afin d’exécuter des tests depuis la ligne de commande, consultez Configurer des tests unitaires.

    Quand vous sélectionnez Analyser la couverture du code, les informations de configuration sont lues à partir du fichier de paramètres d’exécution.

    Conseil

    Les résultats de la couverture du code et la coloration du code précédents ne sont pas masqués automatiquement quand vous exécutez des tests ou que vous mettez à jour votre code.

    Pour activer ou désactiver les paramètres personnalisés, désélectionnez ou sélectionnez le fichier dans le menu Test.

    Pour sélectionner le fichier de paramètres d’exécution, dans le menu Test, choisissez Sélectionner des paramètres de test. Pour spécifier un fichier de paramètres d’exécution afin d’exécuter des tests depuis la ligne de commande, consultez Configurer des tests unitaires.

    Quand vous sélectionnez Analyser la couverture du code, les informations de configuration sont lues à partir du fichier de paramètres d’exécution.

    Conseil

    Les résultats de la couverture du code et la coloration du code précédents ne sont pas masqués automatiquement quand vous exécutez des tests ou que vous mettez à jour votre code.

    Pour activer et désactiver les paramètres personnalisés, choisissez Tester, Configurer les paramètres d’exécution, puis désélectionnez ou sélectionnez le nom du fichier.

Chemins de recherche des symboles

La couverture du code requiert des fichiers de symboles (fichiers .pdb) pour les assemblys. Pour les assemblys générés par votre solution, les fichiers de symboles sont généralement présents à côté des fichiers binaires, et la couverture du code s’exécute automatiquement. Dans certains cas, vous voulez inclure des assemblys référencés dans votre analyse de couverture du code. Dans ce cas, les fichiers .pdb peuvent ne pas être adjacents aux fichiers binaires, mais vous pouvez spécifier le chemin de recherche de symboles dans le fichier .runsettings.

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

Notes

La résolution des symboles peut prendre du temps, surtout quand vous utilisez un emplacement de fichier distant avec de nombreux assemblys. Ainsi, envisagez de copier les fichiers .pdb au même emplacement local que les fichiers binaires (.dll et .exe).

Inclure ou exclure des assemblys et des membres

Vous pouvez inclure ou exclure des assemblys ou des types et membres spécifiques de l’analyse de la couverture du code. Si la section Include est vide ou omise, tous les assemblys qui sont chargés et qui ont des fichiers PDB associés sont inclus. Si un assembly ou un membre correspond à une clause de la section Exclure, il est exclu de la couverture du code. La section Exclude est prioritaire sur la section Include : si un assembly est listé à la fois dans Include et Exclude, il n’est pas inclus dans la couverture du code.

Par exemple, le code XML suivant exclut un assembly en spécifiant son nom :

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

L’exemple suivant spécifie qu’un seul assembly doit être ajouté dans la couverture du code :

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

Le tableau suivant montre les différentes façons dont les assemblys et les membres peuvent être mis en correspondance pour l’inclusion ou l’exclusion dans la couverture du code.

Élément XML Ce à quoi il correspond
ModulePath Correspond aux assemblys spécifiés par le chemin de fichier ou le nom d’assembly.
CompanyName Correspond aux assemblys avec l’attribut Company.
PublicKeyToken Correspond aux assemblys signés par le jeton de clé publique.
Source Correspond à des éléments avec le chemin du fichier source dans lequel ils sont définis.
Attribut Correspond aux éléments qui ont l’attribut spécifié. Spécifiez le nom complet de l’attribut, par exemple <Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>.

Si vous excluez l’attribut CompilerGeneratedAttribute, le code qui utilise des fonctionnalités du langage comme async, await et yield return, et des propriétés implémentées automatiquement, est exclu de l’analyse de couverture du code. Pour exclure le code réellement généré, excluez seulement l’attribut GeneratedCodeAttribute.
Fonction Correspond aux procédures, fonctions ou méthodes avec le nom complet, y compris la liste des paramètres. Vous pouvez également faire correspondre une partie du nom en utilisant une expression régulière.

Exemples :

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

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

Formats de couverture du code

Par défaut, la couverture du code est collectée et enregistrée dans un fichier .coverage. Vous pouvez également collecter la couverture en utilisant d’autres formats, notamment Xml et Cobertura. Différents formats peuvent être utiles dans différents éditeurs et pipelines. Vous pouvez l’activer dans runsettings en ajoutant <Format>Cobertura</Format> ou <Format>Xml</Format> dans la section configuration de DataCollector dans votre fichier runsettings. Ce format peut être vu dans la fenêtre des résultats de la couverture du code dans Visual Studio Enterprise.

Vous pouvez également spécifier différents formats à partir de la ligne de commande en les spécifiant dans le fichier runsettings ou dans un paramètre. Par exemple, la ligne de commande dotnet utilise dotnet test --collect:"Code Coverage;Format=Cobertura". Pour vstest, utilisez vstest.console.exe /collect:"Code Coverage;Format=Cobertura". Le paramètre collect remplace le format spécifié dans runsettings.

Instrumentation native statique et dynamique

Dans Visual Studio 2022 version 17.2, nous avons ajouté la possibilité d’instrumenter le fichier binaire natif de manière statique (sur disque). Dans les versions précédentes, nous prenions en charge uniquement l’instrumentation dynamique, qui n’était souvent pas en mesure d’instrumenter les méthodes. L’instrumentation native statique est plus stable et est recommandée. L’instrumentation native statique nécessite l’activation de l’option de lien /PROFILE pour tous les projets natifs pour lesquels vous avez besoin d’une collection de couverture de code.

Vous pouvez activer l’instrumentation statique native en activant l’instrumentation statique native de couverture du code dans Outils > Options > Environnement > Fonctionnalités en préversion.

Vous pouvez également activer l’instrumentation statique native dans runsettings en ajoutant <EnableStaticNativeInstrumentation>True</EnableStaticNativeInstrumentation> sous la balise <CodeCoverage>. Utilisez cette méthode pour les scénarios de ligne de commande.

Par défaut, l’instrumentation native dynamique est toujours activée. Si l’instrumentation statique et dynamique est activée, Visual Studio tente d’instrumenter votre code C++ de manière statique, mais si cela n’est pas possible (par exemple, quand l’option de lien /PROFILE n’est pas activée), l’instrumentation dynamique est utilisée. Vous pouvez désactiver entièrement l’instrumentation native dynamique dans runsettings en ajoutant <EnableDynamicNativeInstrumentation>False</EnableDynamicNativeInstrumentation> sous <CodeCoverage>.

Quand l’instrumentation native statique est activée, les fichiers binaires natifs sont instrumentés et remplacés sur le disque avant l’exécution du test. Les fichiers binaires d’origine sont restaurés après l’exécution du test. Vous pouvez désactiver la restauration des fichiers d’origine dans runsettings en ajoutant <EnableStaticNativeInstrumentationRestore>False</EnableStaticNativeInstrumentationRestore> sous la balise <CodeCoverage>. Cela peut être particulièrement utile dans les scénarios CI.

Quand l’instrumentation native statique est activée, Visual Studio recherche et instrumente tous les fichiers binaires natifs dans le répertoire où se trouve le fichier binaire de test. Vous pouvez spécifier des répertoires supplémentaires dans lesquels les fichiers binaires doivent être recherchés. L’exemple suivant spécifie que tous les fichiers binaires natifs de C:\temp et de ses sous-répertoires doivent être instrumentés, à l’exception des fichiers se terminant par Fabrikam.Math.dll.

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

Expressions régulières

Les nœuds Inclure et Exclure utilisent des expressions régulières, qui ne sont pas identiques à des caractères génériques. Les correspondances ne respectent pas la casse. Quelques exemples :

  • .* correspond à une chaîne de n’importe quels caractères

  • \. correspond à un point « . »

  • \( \) correspond à des parenthèses « ( ) »

  • \\ correspond à un séparateur de chemin de fichier « \ »

  • ^ correspond au début de la chaîne

  • $ correspond à la fin de la chaîne

Le code XML suivant montre comment inclure et exclure des assemblys spécifiques en utilisant des expressions régulières :

<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>

Le code XML suivant montre comment inclure et exclure des fonctions spécifiques en utilisant des expressions régulières :

<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>

Avertissement

S’il existe une erreur dans une expression régulière, telle qu’une séquence d’échappement ou une parenthèse sans correspondance, l’analyse de couverture du code ne fonctionne pas.

Pour plus d’informations sur les expressions régulières, consultez Utiliser des expressions régulières dans Visual Studio.

Fichier d'exemple .runsettings

Copiez ce code et modifiez-le selon vos besoins.

<?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>