Condividi tramite


Personalizzare l'analisi della cobertura del codice

Per impostazione predefinita, code coverage analizza tutti gli assembly di soluzione caricati durante gli unit test. È consigliabile usare questo comportamento predefinito, perché funziona correttamente nella maggior parte dei casi. Per altre informazioni, vedere Usare code coverage per determinare la quantità di codice testata.

Per escludere il codice di test dai risultati del code coverage e includere solo il codice dell'applicazione, aggiungere l'attributo ExcludeFromCodeCoverageAttribute alla classe di test.

Per includere assembly che non fanno parte della tua soluzione, ottieni i file .pdb per questi assembly e copiali nella stessa cartella dei file dell'assembly .dll.

File delle impostazioni di esecuzione

Il file delle impostazioni di esecuzione è il file di configurazione usato dagli strumenti di unit test. Le impostazioni di code coverage avanzate vengono specificate in un file runsettings.

Per personalizzare il code coverage, seguire questa procedura:

  1. Aggiungere un file di impostazioni di esecuzione alla soluzione. In Esplora soluzioni, dal menu di scelta rapida della soluzione, scegli Aggiungi>Nuovo Elementoe seleziona File XML. Salvare il file con un nome, ad esempio CodeCoverage.runsettings.

    Se non vengono visualizzati tutti i modelli di elemento, scegliere Mostra tutti i modellie quindi scegliere il modello di elemento.

  2. Aggiungere il contenuto del file di esempio alla fine di questo articolo e quindi personalizzarlo in base alle esigenze, come descritto nelle sezioni seguenti.

  3. Selezionare un file di impostazioni di esecuzione.

    A partire da Visual Studio 2019 versione 16.4, è possibile rilevare automaticamente un file di impostazioni di esecuzione nella directory principale del progetto. In caso contrario, nel menu Test scegliere Configura impostazioni di esecuzione, e quindi scegliere Seleziona file runsettings a livello di soluzione. Per specificare un file di impostazioni di esecuzione per l'esecuzione di test dalla riga di comando, vedere Configurare unit test.

    Quando selezioni Analizza copertura del codice, le informazioni di configurazione vengono lette dal file delle impostazioni di esecuzione.

    Suggerimento

    I risultati della copertura del codice e la colorazione del codice precedenti non vengono nascosti automaticamente quando si eseguono test o si aggiorna il codice.

    Per disattivare e attivare le impostazioni personalizzate, deselezionare o selezionare il file nel menu Test.

    Per selezionare il file delle impostazioni di esecuzione, nel menu Test, scegliere Seleziona file di impostazioni. Per specificare un file di impostazioni di esecuzione per l'esecuzione di test dalla riga di comando, vedere Configurare unit test.

    Quando si seleziona Analizza copertura del codice, le informazioni di configurazione vengono lette dal file delle impostazioni di esecuzione.

    Consiglio

    I risultati della copertura del codice e la colorazione del codice precedenti non vengono nascosti automaticamente quando si eseguono test o quando si aggiorna il codice.

    Per attivare e disattivare le impostazioni personalizzate, scegliere Test, Configurare impostazioni di esecuzionee deselezionare o selezionare il nome del file.

Percorsi di ricerca dei simboli

Il code coverage richiede file di simboli (file con estensione pdb) per gli assembly. Per gli assembly compilati dalla soluzione, i file di simboli sono in genere presenti insieme ai file binari e il code coverage funziona automaticamente. In alcuni casi, potrebbe essere necessario includere assembly a cui si fa riferimento nell'analisi della copertura del codice. In tali casi, i file .pdb potrebbero non essere adiacenti ai file binari, ma è possibile specificare il percorso di ricerca dei simboli nel file .runsettings .

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

Nota

La risoluzione dei simboli può richiedere tempo, soprattutto quando si usa un percorso di file remoto con molti assembly. È pertanto consigliabile copiare file con estensione pdb nello stesso percorso locale dei file binari (.dll e .exe).

Includere o escludere assembly e membri

È possibile includere o escludere assembly o tipi e membri specifici dall'analisi della copertura del codice. Se la sezione Includi è vuota o omessa, vengono inclusi tutti gli assembly che sono caricati e i file PDB associati. Se un assembly o un membro corrisponde a una clausola nella sezione Exclude, vengono esclusi dalla copertura del codice. La sezione Escludi ha la precedenza rispetto alla sezione Includi: se un assembly è elencato sia in Includi che in Escludi, non verrà incluso nella copertura del codice.

Ad esempio, il codice XML seguente esclude un singolo assembly specificandone il nome:

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

Nell'esempio seguente viene specificato che è necessario includere solo un singolo assembly nel code coverage:

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

La tabella seguente mostra i vari modi in cui gli assembly e i membri possono essere abbinati per l'inclusione o l'esclusione dalla copertura del codice.

Elemento XML Elementi corrispondenti
ModulePath Corrisponde agli assembly specificati dal nome dell'assembly o dal percorso del file.
CompanyName Abbina gli assembly in base all'attributo Company.
PublicKeyToken Confronta gli assembly firmati tramite il token della chiave pubblica.
Fonte Trova la corrispondenza degli elementi in base al nome del percorso del file di origine in cui sono definiti.
Attributo Trova la corrispondenza degli elementi con l'attributo specificato. Specificare il nome completo dell'attributo, ad esempio <Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>.

Se si esclude l'attributo CompilerGeneratedAttribute, il codice che usa funzionalità del linguaggio come async, await, yield returne le proprietà implementate automaticamente vengono escluse dall'analisi del code coverage. Per escludere il codice effettivamente generato, escludere solo l'attributo GeneratedCodeAttribute.
Funzione Associa procedure, funzioni o metodi per nome completo, inclusa la lista dei parametri. È anche possibile trovare una corrispondenza con parte del nome usando un'espressione regolare .

Esempi:

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

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

Formati di code coverage

Per impostazione predefinita, il code coverage viene raccolto e salvato in un file .coverage. È anche possibile raccogliere copertura usando altri formati, tra cui Xml e Cobertura. I diversi formati possono essere utili in diversi editor e pipeline. È possibile abilitarlo in runsettings aggiungendo <Format>Cobertura</Format> o <Format>Xml</Format> nella sezione di configurazione DataCollector nel file runsettings. Questo formato può essere visualizzato nella finestra dei risultati della copertura del codice in Visual Studio Enterprise.

È anche possibile specificare formati diversi dalla riga di comando specificandolo nel file runsettings o specificandolo in un parametro . Ad esempio, la riga di comando dotnet usa dotnet test --collect:"Code Coverage;Format=Cobertura". Per vstest usare vstest.console.exe /collect:"Code Coverage;Format=Cobertura". Il parametro collect sovrascriverà il formato specificato in runsettings.

Strumentazione nativa statica e dinamica

In Visual Studio 2022 versione 17.2 è stata aggiunta l'opzione per instrumentare il file binario nativo in modo statico (su disco). Nelle versioni precedenti è stata supportata solo la strumentazione dinamica, che spesso non è stata in grado di instrumentare i metodi. La strumentazione nativa statica è più stabile ed è consigliabile. La strumentazione nativa statica richiede che l'opzione di collegamento /PROFILE sia abilitata per tutti i progetti nativi per cui è necessaria la raccolta della copertura del codice.

È anche possibile abilitare la strumentazione statica nativa in runsettings aggiungendo <EnableStaticNativeInstrumentation>True</EnableStaticNativeInstrumentation> sotto <CodeCoverage> tag. Usare questo metodo per gli scenari della riga di comando.

Per impostazione predefinita, la strumentazione nativa dinamica è sempre abilitata. Se la strumentazione statica e dinamica è abilitata, Visual Studio tenta di instrumentare il codice C++ in modo statico, ma se non è possibile (ad esempio, quando l'opzione di collegamento /PROFILE non è abilitata), verrà usata la strumentazione dinamica. È possibile disabilitare completamente la strumentazione nativa dinamica in runsettings aggiungendo <EnableDynamicNativeInstrumentation>False</EnableDynamicNativeInstrumentation> in <CodeCoverage>.

Quando la strumentazione nativa statica è abilitata, i file binari nativi verranno instrumentati e sostituiti sul disco prima dell'esecuzione del test. I file binari originali verranno ripristinati dopo l'esecuzione del test. È possibile disabilitare il ripristino dei file originali in runsettings aggiungendo <EnableStaticNativeInstrumentationRestore>False</EnableStaticNativeInstrumentationRestore> sotto il tag <CodeCoverage>. Questo può risultare particolarmente utile negli scenari di integrazione continua.

Quando la strumentazione nativa statica è abilitata, Visual Studio cercherà e instrumenterà tutti i file binari nativi nella directory in cui si trova il file binario di test. È possibile specificare directory aggiuntive in cui devono essere cercati i file binari. Nell'esempio seguente viene specificato che tutti i file binari nativi di C:\temp e le relative sottodirectory devono essere instrumentati ad eccezione dei file che terminano con Fabrikam.Math.dll.

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

Espressioni regolari

I nodi di inclusione ed esclusione utilizzano espressioni regolari, che non sono uguali ai caratteri jolly. Tutte le corrispondenze sono insensibili alla distinzione tra maiuscole e minuscole. Ecco alcuni esempi:

  • .* corrisponde a una stringa di qualsiasi carattere

  • \. corrisponde a un punto "."

  • \( \) corrisponde alle parentesi "( )"

  • \\ corrisponde a un delimitatore di percorso file "\"

  • ^ corrisponde all'inizio della stringa

  • $ corrisponde alla fine della stringa

Il codice XML seguente illustra come includere ed escludere assembly specifici usando espressioni regolari:

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

Il codice XML seguente illustra come includere ed escludere funzioni specifiche usando espressioni regolari:

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

Avvertimento

Se si verifica un errore in un'espressione regolare, ad esempio una parentesi non sfuggita o non corrispondente, l'analisi della copertura del codice non verrà eseguita.

Per altre informazioni sulle espressioni regolari, vedere Usare espressioni regolari in Visual Studio.

File di esempio .runsettings

Copiare questo codice e modificarlo in base alle proprie esigenze.

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