Personalizar el análisis de cobertura de código

De forma predeterminada, la cobertura de código analiza todos los ensamblados de la solución que se cargan durante las pruebas unitarias. Se recomienda usar este comportamiento de forma predeterminada, porque funciona bien la mayoría de los casos. Para obtener más información, vea Usar cobertura de código para determinar la cantidad de código que se está probando.

Para excluir el código de prueba de los resultados de cobertura de código e incluir solo código de la aplicación, agregue el atributo ExcludeFromCodeCoverageAttribute a la clase de prueba.

Para incluir ensamblados que no forman parte de la solución, obtenga los archivos .pdb para los ensamblados y cópielos en la misma carpeta que los archivos de ensamblado .dll.

Archivo de parámetros de ejecución

Es el archivo de parámetros de ejecución que usan las herramientas de pruebas unitarias. La configuración avanzada de la cobertura de código se especifica en un archivo .runsettings.

Para personalizar la cobertura de código, siga estos pasos:

  1. Agregue un archivo de parámetros de ejecución a la solución. En el Explorador de soluciones, en el menú contextual de la solución, seleccione Agregar>Nuevo elemento y seleccione Archivo XML. Guarde el archivo con un nombre como CodeCoverage.runsettings.

    Si no ve todas las plantillas de elemento, seleccione Mostrar todas las plantillas y elija la plantilla de elemento.

  2. Agregue el contenido que se muestra en el archivo de ejemplo al final del artículo y después personalícelo de acuerdo con sus necesidades como se describe en las secciones siguientes.

  3. Seleccione un archivo de parámetros de ejecución.

    A partir de la versión 16.4 de Visual Studio 2019, puede detectar automáticamente un archivo de parámetros de ejecución en la raíz del proyecto. De lo contrario, en el menú Prueba, elija Configurar parámetros de ejecución y, después, elija Seleccionar archivo de parámetros de ejecución para todo la solución. Para especificar un archivo de parámetros de ejecución para ejecutar pruebas desde la línea de comandos, vea Configuración de pruebas unitarias.

    Al seleccionar Analizar cobertura de código, la información de configuración se lee desde el archivo de parámetros de ejecución.

    Sugerencia

    Los anteriores resultados de cobertura y colores de código no se ocultan automáticamente al ejecutar pruebas o actualizar el código.

    Para desactivar y activar la configuración personalizada, anule la selección o seleccione el archivo en el menú Prueba.

    Para seleccionar el archivo de parámetros de ejecución, en el menú Prueba, elija Seleccionar archivo de configuración. Para especificar un archivo de parámetros de ejecución para ejecutar pruebas desde la línea de comandos, vea Configuración de pruebas unitarias.

    Al seleccionar Analizar cobertura de código, la información de configuración se lee desde el archivo de parámetros de ejecución.

    Sugerencia

    Los anteriores resultados de cobertura y colores de código no se ocultan automáticamente al ejecutar pruebas o actualizar el código.

    Para desactivar y activar los parámetros de ejecución, elija Probar, Configurar parámetros de ejecución y anule la selección o seleccione el nombre de archivo.

Rutas de acceso de búsqueda de símbolos

La cobertura de código requiere que haya símbolos (archivos .pdb) para los ensamblados. En el caso de los ensamblados compilados por su solución, los archivos de símbolos normalmente están presentes con los archivos binarios y la cobertura de código funciona automáticamente. En algunos casos, puede que le interese incluir los ensamblados a los que se hace referencia en el análisis de cobertura de código. En esos casos, los archivos .pdb podrían no estar adyacentes a los archivos binarios pero puede especificar la ruta de búsqueda de símbolos en el archivo .runsettings.

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

Nota

La resolución de símbolos puede tardar tiempo, especialmente al utilizar una ubicación de archivo remota con muchos ensamblados. Por tanto, considere la posibilidad de copiar los archivos .pdb en la misma ubicación local que los archivos binarios ( .dll y .exe).

Inclusión o exclusión de ensamblados y miembros

Puede incluir o excluir ensamblados o tipos y miembros específicos del análisis de cobertura de código. Si la sección Include está vacía o se omite, se incluyen todos los ensamblados que se cargan y que tienen archivos PDB asociados. Si un ensamblado o un miembro coincide con una cláusula de la sección Exclude, se excluye de la cobertura de código. La sección Exclude tiene prioridad sobre la sección Include. Si un ensamblado aparece tanto en Include como en Exclude, no se incluirá en la cobertura de código.

Por ejemplo, el siguiente código XML excluye un solo ensamblado mediante la especificación de su nombre:

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

En el ejemplo siguiente se especifica que solo se debe incluir un ensamblado en la cobertura de código:

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

En la tabla siguiente se muestran las distintas formas en las que se pueden hacer coincidir los ensamblados y los miembros para incluirlos en la cobertura de código o excluirlos de ella.

Elemento XML Con qué busca coincidencias
ModulePath Busca coincidencias con los ensamblados especificados por el nombre o la ruta de acceso del ensamblado.
CompanyName Busca coincidencias con ensamblados por el atributo Compañía.
PublicKeyToken Busca coincidencias con ensamblados firmados por el token de clave pública.
Origen Busca coincidencias con los elementos por el nombre de ruta de acceso del archivo de código fuente en el cual se definen.
Atributo Busca coincidencias con los elementos que tienen el atributo especificado. Especifique el nombre completo del atributo, por ejemplo, <Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>.

Si excluye el atributo CompilerGeneratedAttribute, el código que usa características del lenguaje como async, await, yield return y propiedades implementadas automáticamente se excluye del análisis de cobertura de código. Para excluir el código generado realmente, excluya solo el atributo GeneratedCodeAttribute.
Función Busca coincidencias de procedimientos, funciones o métodos por el nombre completo, incluida la lista de parámetros. También puede buscar coincidencias con parte del nombre mediante el uso de una expresión regular.

Ejemplos:

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

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

Formatos de cobertura de código

De forma predeterminada, la cobertura de código se recopila y guarda en un archivo .coverage. También puede recopilar cobertura mediante otros formatos, como XML y Cobertura. Los distintos formatos pueden ser útiles en diferentes editores y canalizaciones. Puede habilitar esta opción en runsettings agregando <Format>Cobertura</Format> o <Format>Xml</Format> en la sección de configuración de DataCollector del archivo runsettings. Este formato se puede ver en la ventana de resultados de cobertura de código de Visual Studio Enterprise.

También puede especificar formatos diferentes de la línea de comandos si lo especifica en el archivo runsettings o en un parámetro. Por ejemplo, para la línea de comandos de dotnet, use dotnet test --collect:"Code Coverage;Format=Cobertura". Para vstest, use vstest.console.exe /collect:"Code Coverage;Format=Cobertura". El parámetro collect reemplazará el formato especificado en runsettings.

Instrumentación nativa estática y dinámica

En Visual Studio 2022 versión 17.2, hemos agregado la opción para instrumentar binario nativo estáticamente (en disco). En versiones anteriores, solo se admite la instrumentación dinámica, que a menudo no era capaz de instrumentar métodos. La instrumentación nativa estática es más estable y se recomienda. La instrumentación nativa estática requiere habilitar la opción de vínculo /PROFILE para todos los proyectos nativos para los que necesita la recopilación de cobertura de código.

Puede habilitar la instrumentación estática nativa habilitando la Característica en vista previa (GB) Code Coverage native static instrumentation (Instrumentación estática nativa de cobertura de código) en Herramientas > Opciones > Entorno > Característica en vista previa.

También puede habilitar la instrumentación estática nativa en runsettings agregando <EnableStaticNativeInstrumentation>True</EnableStaticNativeInstrumentation> bajo la etiqueta <CodeCoverage>. Use este método para escenarios de línea de comandos.

De forma predeterminada, la instrumentación nativa dinámica siempre está habilitada. Si la instrumentación estática y dinámica está habilitada, Visual Studio intenta instrumentar el código de C++ estáticamente, pero si esto no es posible (por ejemplo, cuando la opción de vínculo /PROFILE no está habilitada), se usará la instrumentación dinámica. Puede deshabilitar completamente la instrumentación nativa dinámica en runsettings agregando <EnableDynamicNativeInstrumentation>False</EnableDynamicNativeInstrumentation> en <CodeCoverage>.

Cuando la instrumentación nativa estática está habilitada, los archivos binarios nativos se instrumentarán y reemplazarán en el disco antes de la ejecución de la prueba. Los archivos binarios originales se restaurarán después de la ejecución de la prueba. Puede deshabilitar la restauración de archivos originales en runsettings agregando <EnableStaticNativeInstrumentationRestore>False</EnableStaticNativeInstrumentationRestore> bajo la etiqueta <CodeCoverage>. Esto puede ser especialmente útil en escenarios de CI.

Cuando se habilita la instrumentación nativa estática, Visual Studio buscará e instrumentará todos los archivos binarios nativos en el directorio donde se encuentra el binario de prueba. Puede especificar directorios adicionales en los que se deben buscar archivos binarios. En el ejemplo siguiente se especifica que todos los archivos binarios nativos de C:\temp y sus subdirectorios se deben instrumentar excepto los archivos que terminan con Fabrikam.Math.dll.

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

Expresiones regulares

Los nodos de inclusión y exclusión usan expresiones regulares, que no son iguales que los caracteres comodín. Ninguna coincidencia distingue entre mayúsculas y minúsculas. Ejemplos:

  • .* coincide con una cadena con cualquier carácter

  • \. coincide con un punto (".")

  • \( \) coincide con paréntesis "( )"

  • \\ coincide con el delimitador de ruta de acceso "\"

  • ^ coincide con el inicio de la cadena

  • $ coincide con el final de la cadena

En el siguiente código XML se muestra cómo incluir y excluir ensamblados específicos mediante expresiones regulares:

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

En el siguiente código XML se muestra cómo incluir y excluir funciones específicas mediante expresiones regulares:

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

Advertencia

Si hay un error en una expresión regular, como paréntesis sin caracteres de escape o sin su correspondiente pareja, el análisis de cobertura de código no se ejecutará.

Para obtener más información sobre las expresiones regulares, consulte Usar expresiones regulares en Visual Studio.

Archivo de ejemplo .runsettings

Copie el código y edítelo para satisfacer sus necesidades.

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