Compartir vía


Personalizar el análisis de cobertura de código

De forma predeterminada, la cobertura de código analiza todos los ensamblados de solución que se cargan durante las pruebas unitarias. Se recomienda usar este comportamiento predeterminado, ya que funciona bien la mayor parte del tiempo. 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 el código de aplicación, agregue el atributo ExcludeFromCodeCoverageAttribute a la clase de prueba.

Para incluir ensamblajes que no son parte de su solución, obtenga los archivos .pdb de para estos ensamblajes y cópielos en la misma carpeta que los archivos de ensamblaje .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 configuración de ejecución a la solución. En Explorador de Soluciones, en el menú contextual de la solución, elija Agregar>Nuevo Elementoy seleccione Archivo XML. Guarde el archivo con un nombre como CodeCoverage.runsettings.

    Si no ve todas las plantillas de elemento, elija Mostrar todas las plantillasy, a continuación, elija la plantilla de elemento.

  2. Agregue el contenido del archivo de ejemplo al final de este artículo y personalícelo a sus necesidades, tal como se describe en las secciones siguientes.

  3. Seleccione un archivo de configuración de ejecución.

    A partir de la versión 16.4 de Visual Studio 2019, puede detectar automáticamente un archivo de configuración 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 configuración de ejecución para ejecutar pruebas desde la línea de comandos, consulte Configurar 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 resultados anteriores de cobertura de código y el color 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 configuración de ejecución para ejecutar pruebas desde la línea de comandos, consulte Configurar 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 resultados anteriores de cobertura de código y el color 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 creados por la solución, los archivos de símbolos suelen estar presentes junto 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 mucho tiempo, especialmente cuando se usa una ubicación de archivo remota con muchos ensamblados. Por lo tanto, considere la posibilidad de copiar 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 Incluir está vacía o se omite, se incluyen todos los ensamblados que se cargan y 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 Excluir tiene prioridad sobre la sección Incluir: si un ensamblaje aparece en ambas, Incluir y Excluir, no se incluirá en la cobertura de código.

Por ejemplo, el siguiente XML excluye un único ensamblado especificando 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 único 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.
Fuente Busca coincidencias con los elementos por el nombre de ruta de acceso del archivo de código fuente en el cual se definen.
Atributo Coincide 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 de lenguaje como async, await, yield returny las propiedades implementadas automáticamente se excluyen del análisis de cobertura de código. Para excluir código realmente generado, solo excluya el atributo GeneratedCodeAttribute.
Función Identifica procedimientos, funciones o métodos mediante el nombre completo, incluyendo 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 con otros formatos, como Xml y Cobertura. Los distintos formatos pueden ser útiles en diferentes editores y canalizaciones. Puede habilitarlo 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 en Visual Studio Enterprise.

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

Instrumentación nativa estática y dinámica

En la versión 17.2 de Visual Studio 2022, se agregó la opción para instrumentar binario nativo estáticamente (en disco). En versiones anteriores, solo se admitía 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.

También puede habilitar la instrumentación estática nativa en el archivo de configuración runsettings agregando <EnableStaticNativeInstrumentation>True</EnableStaticNativeInstrumentation> en 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> en 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 deben instrumentarse, 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. Algunos ejemplos son:

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

El siguiente XML 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>

El siguiente XML 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, vea Usar expresiones regulares en Visual Studio.

Archivo .runsettings de ejemplo

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