Personalizzare l'analisi code coverage

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

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 soluzione, ottenere i file PDB per tali assembly e copiarli nella stessa cartella dei file DLL dell'assembly.

File di impostazioni esecuzione test

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

Per personalizzare il code coverage, seguire questa procedura:

  1. Aggiungere un file di impostazioni esecuzione test alla propria soluzione. In Esplora soluzioni scegliere Aggiungi>Nuovo elemento dal menu di scelta rapida della soluzione e selezionare File XML. Salvare il file con un nome come CodeCoverage.runsettings.

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

  2. Aggiungere il contenuto riportato nel file di esempio alla fine di questo articolo, quindi personalizzarlo secondo le proprie esigenze come descritto nelle sezioni seguenti.

  3. Selezionare un file di impostazioni di esecuzione.

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

    Quando si seleziona Analizza code coverage le informazioni di configurazione vengono lette dal file di impostazioni esecuzione test.

    Suggerimento

    Gli eventuali risultati del code coverage precedente e la colorazione del codice non vengono nascosti automaticamente quando si eseguono i test o si aggiorna il codice.

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

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

    Quando si seleziona Analizza code coverage le informazioni di configurazione vengono lette dal file di impostazioni esecuzione test.

    Suggerimento

    Gli eventuali risultati del code coverage precedente e la colorazione del codice non vengono nascosti automaticamente quando si eseguono i test o si aggiorna il codice.

    Per disattivare le impostazioni personalizzate e attivare, scegliere Test, Configura esegui Impostazioni e deselezionare o selezionare il nome del file.

Percorsi di ricerca dei simboli

Il code coverage richiede file di simboli (file PDB) per gli assembly. Per gli assembly compilati dalla soluzione, i file di simboli sono solitamente presenti accanto ai file binari e il code coverage viene eseguito automaticamente. In alcuni casi è consigliabile includere gli assembly a cui si fa riferimento nell'analisi del code coverage. In questi casi i file PDB non possono essere adiacenti ai 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, in particolare quando si usa un percorso file remoto con molti assembly. Di conseguenza, si consiglia di copiare i file PDB nello stesso percorso locale dei file binari (DLL ed EXE).

Includere o escludere assembly e membri

È possibile includere o escludere assembly o tipi e membri specifici dall'analisi del code coverage. Se la sezione Includi è vuota o omessa, vengono inclusi tutti gli assembly caricati e i file PDB associati. Se un assembly o un membro corrisponde a una clausola nella sezione Exclude , viene esclusa dal code coverage. La sezione Exclude ha la precedenza sulla sezione Includi: se un assembly è elencato sia in Includi che in Escludi, non verrà incluso nel code coverage.

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 illustra i vari modi in cui gli assembly e i membri possono essere confrontati per l'inclusione o l'esclusione dal code coverage.

Elemento XML Elementi corrispondenti
ModulePath Corrisponde agli assembly specificati dal nome dell'assembly o dal percorso del file.
CompanyName Corrisponde agli assembly in base all'attributo Company .
PublicKeyToken Corrisponde agli assembly firmati dal token di chiave pubblica.
Origine 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 return e le proprietà implementate automaticamente viene escluso dall'analisi code coverage. Per escludere il codice effettivamente generato, escludere solo l'attributo GeneratedCodeAttribute.
Funzione Corrisponde a procedure, funzioni o metodi in base al nome completo, incluso l'elenco di 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 .coverage file. È 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 di DataCollector nel file runsettings. Questo formato può essere visualizzato nella finestra dei risultati del code coverage 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 eseguirà l'override del 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 l'abilitazione dell'opzione di collegamento /PROFILE per tutti i progetti nativi per cui è necessaria la raccolta di code coverage.

È possibile abilitare la strumentazione statica nativa abilitando la funzionalità di anteprima della strumentazione statica nativa code coverage in Strumenti > Opzioni > Funzionalità di anteprima dell'ambiente>.

È 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 non è abilitata), verrà usata la /PROFILE 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 <CodeCoverage> tag . Ciò può essere 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 da 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 Includi ed Escludi usano espressioni regolari, che non sono uguali ai caratteri jolly. Tutte le corrispondenze fanno distinzione tra maiuscole e minuscole. Alcuni esempi sono:

  • .* corrisponde a una stringa composta da caratteri qualsiasi

  • \. 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>

Avviso

Se è presente un errore in un'espressione regolare, ad esempio un carattere senza codice di escape o parentesi non corrispondenti, l'analisi di code coverage non funzionerà.

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

File con estensione runsettings di esempio

Copiare questo codice e modificarlo in base alle 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>
<!--
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>