Personalizar a análise de cobertura de código

Por padrão, a cobertura de código analisa todos os assemblies de solução que são carregados durante os testes de unidade. Recomendamos que esse comportamento padrão seja mantido, pois geralmente ele funciona bem. Para obter mais informações, confira Usar a cobertura de código para determinar quanto do código está sendo testado.

Para excluir o código de teste dos resultados da cobertura de código e incluir apenas o código do aplicativo, adicione o atributo ExcludeFromCodeCoverageAttribute à classe de teste.

Para incluir os assemblies que não fazem parte da solução, obtenha os arquivos .pdb desses assemblies e copia-os na mesma pasta que os arquivos .dll do assembly.

Arquivo de configurações de execução

O arquivo de configurações de execução é o arquivo de configuração usado pelas ferramentas de teste da unidade. As configurações avançadas da cobertura de código são especificadas em um arquivo .runsettings.

Para personalizar a cobertura de código, siga estas etapas:

  1. Adicione um arquivo de configurações de execução à sua solução. Na Gerenciador de Soluções, no menu de atalho da solução, escolha Adicionar>Novo Item e selecione arquivo XML. Salve o arquivo com um nome como CodeCoverage.runsettings.

    Caso você não veja todos os modelos de item, escolha Mostrar todos os modelos e escolha o modelo de item.

  2. Adicione o conteúdo do arquivo de exemplo no final deste artigo e personalize-o de acordo com suas necessidades, conforme descrito nas seções a seguir.

  3. Selecione um arquivo de configurações de execução.

    A partir do Visual Studio 2019 versão 16.4, você pode detectar automaticamente um arquivo de configurações de execução na raiz do projeto. Caso contrário, no menu Testar, escolha Definir as Configurações de Execução e selecione Selecionar o Arquivo de configurações de execução Ampla da Solução. Para especificar um arquivo de configurações de execução para executar testes na linha de comando, confira Configurar testes de unidade.

    Quando você seleciona Analisar Cobertura de Código, as informações de configuração são lidas no arquivo de configurações de execução.

    Dica

    Os resultados da cobertura de código e a coloração de código anteriores não são ocultados automaticamente quando você executa testes ou atualiza o código.

    Para ativar ou desativar as configurações personalizadas, desmarque ou selecione o arquivo no menu Teste.

    Para selecionar o arquivo de configurações de execução, no menu Testar, escolha Selecionar Arquivo de Configurações. Para especificar um arquivo de configurações de execução para executar testes na linha de comando, confira Configurar testes de unidade.

    Quando você seleciona Analisar Cobertura de Código, as informações de configuração são lidas no arquivo de configurações de execução.

    Dica

    Os resultados da cobertura de código e a coloração de código anteriores não são ocultados automaticamente quando você executa testes ou atualiza o código.

    Para desativar e ativar as configurações personalizadas, escolha Teste, Definir as Configurações de Execução e desmarque ou selecione o nome do arquivo.

Caminhos de pesquisa de símbolos

A cobertura de código exige arquivos de símbolo (arquivos .pdb) para assemblies. Para assemblies compilados por sua solução, os arquivos de símbolos estão geralmente presentes nos arquivos binários e a cobertura de código funciona automaticamente. Em alguns casos, o ideal é incluir os assemblies referenciados na análise de cobertura de código. Nesses casos, os arquivos .pdb podem não estar adjacentes aos binários, mas você pode especificar o caminho de pesquisa de símbolos no arquivo .runsettings.

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

Observação

A resolução de símbolos pode ser demorada, especialmente ao usar um local de arquivo remoto com muitos assemblies. Portanto, considere copiar os arquivos .pdb no mesmo local que os arquivos binários (.dll e .exe).

Incluir ou excluir assemblies e membros

Você pode incluir ou excluir assemblies ou tipos específicos e membros da análise de cobertura de código. Se a seção Incluir estiver vazia ou omitida, todos os assemblies carregados e com arquivos PDB associados serão incluídos. Se um assembly ou membro corresponder a uma cláusula na seção Excluir, ele será excluído da cobertura de código. A seção Excluir tem precedência sobre a seção Incluir: se um assembly estiver listado em Incluir e Excluir, ele não será incluído na cobertura de código.

Por exemplo, o XML a seguir exclui apenas um assembly especificando o nome dele:

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

O seguinte exemplo especifica que apenas um assembly deve ser incluído na cobertura de código:

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

A tabela a seguir mostra as várias maneiras pelas quais assemblies e membros podem ser correspondidos para inclusão ou exclusão da cobertura de código.

Elemento XML Que correspondências ele realiza
ModulePath Faz a correspondência entre os assemblies especificados pelo nome do assembly ou caminho do arquivo.
CompanyName Faz a correspondência de assemblies pelo atributo Company.
PublicKeyToken Faz a correspondência de assemblies assinados pelo token de chave pública.
Fonte Faz a correspondência de elementos pelo nome do caminho do arquivo de origem no qual eles são definidos.
Atributo Faz a correspondência de elementos que têm o atributo especificado. Especifique o nome completo do atributo, por exemplo, <Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>.

Se você excluir o atributo CompilerGeneratedAttribute, o código que usa recursos de linguagem, como async, await, yield return e as propriedades implementadas automaticamente serão excluídas da análise de cobertura de código. Para excluir o código verdadeiramente gerado, exclua apenas o atributo GeneratedCodeAttribute.
Função Faz a correspondência de procedimentos, funções ou métodos por nome totalmente qualificado, incluindo a lista de parâmetros. Você também pode fazer a correspondência de parte do nome usando uma expressão regular.

Exemplos:

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

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

Formatos de cobertura de código

Por padrão, a cobertura de código é coletada e salva em um arquivo .coverage. Você também pode coletar a cobertura usando outros formatos, incluindo Xml e Cobertura. Formatos diferentes podem ser úteis em diferentes editores e pipelines. Você pode habilitar isso em runsettings adicionando <Format>Cobertura</Format> ou <Format>Xml</Format> na seção Configuração do DataCollector no arquivo runsettings. Esse formato pode ser exibido na janela de resultados da cobertura de código em Visual Studio Enterprise.

Você também pode especificar formatos diferentes da linha de comando especificando-os no arquivo runsettings ou especificando-os em um parâmetro. Por exemplo, a linha de comando dotnet usa dotnet test --collect:"Code Coverage;Format=Cobertura". Para vstest, use vstest.console.exe /collect:"Code Coverage;Format=Cobertura". O parâmetro collect substituirá o formato especificado em runsettings.

Instrumentação nativa estática e dinâmica

No Visual Studio 2022 versão 17.2, adicionamos a opção de instrumentar binário nativo estaticamente (em disco). Nas versões anteriores, tínhamos suporte apenas para instrumentação dinâmica, que muitas vezes não era capaz de instrumentar métodos. A instrumentação nativa estática é mais estável e é recomendável. A instrumentação nativa estática requer a habilitação da opção de link /PROFILE para todos os projetos nativos para os quais você precisa de uma coleta de dados de cobertura de código.

Você pode habilitar a instrumentação estática nativa habilitando a versão prévia do recurso Instrumentação estática nativa da Cobertura de Código em Ferramentas > Opções > Ambiente > Versão prévia dos recursos.

Você também pode habilitar a instrumentação estática nativa em runsettings adicionando <EnableStaticNativeInstrumentation>True</EnableStaticNativeInstrumentation> sob a marca <CodeCoverage>. Use esse método para cenários de linha de comando.

Por padrão, a instrumentação nativa dinâmica está sempre habilitada. Se a instrumentação estática e dinâmica estiver habilitada, o Visual Studio tentará instrumentar seu código C++ estaticamente, mas se isso não for possível (por exemplo, quando a opção de link /PROFILE não estiver habilitada), a instrumentação dinâmica será usada. Você pode desabilitar totalmente a instrumentação nativa dinâmica em runsettings adicionando <EnableDynamicNativeInstrumentation>False</EnableDynamicNativeInstrumentation> em <CodeCoverage>.

Quando a instrumentação nativa estática estiver habilitada, os binários nativos serão instrumentados e substituídos no disco antes da execução do teste. Os binários originais serão restaurados após a execução do teste. Você pode desabilitar a restauração de arquivos originais em runsettings adicionando <EnableStaticNativeInstrumentationRestore>False</EnableStaticNativeInstrumentationRestore> sob a marca <CodeCoverage>. Isso pode ser especialmente útil em cenários de CI.

Quando a instrumentação nativa estática estiver habilitada, o Visual Studio pesquisará e instrumentará todos os binários nativos no diretório em que o binário de teste está localizado. Você pode especificar diretórios adicionais em que os binários devem ser pesquisados. O exemplo a seguir especifica que todos os binários nativos de C:\temp e os respectivos subdiretórios devem ser instrumentados, exceto arquivos que terminam com Fabrikam.Math.dll.

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

Expressões regulares

Os nós de inclusão e exclusão usam expressões regulares, que não são iguais aos curingas. Todas as correspondências não diferenciam maiúsculas de minúsculas. Alguns exemplos incluem:

  • .* corresponde a uma cadeia de caracteres quaisquer

  • \. corresponde a um ponto "."

  • \( \) corresponde a parênteses "( )"

  • \\ corresponde a um delimitador de caminho de arquivo "\"

  • ^ corresponde ao início da cadeia de caracteres

  • $ corresponde ao final da cadeia de caracteres

O seguinte XML mostra como incluir e excluir assemblies específicos usando expressões 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>

O seguinte XML mostra como incluir e excluir funções específicas usando expressões 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>

Aviso

Se houver um erro em uma expressão regular, como um parêntese sem correspondência ou sem escape, a análise de cobertura de código não será executada.

Para obter mais informações sobre expressões regulares, confira Usar expressões regulares no Visual Studio.

Arquivo .runsettings de exemplo

Copie esse código e edite-o para atender às suas necessidades.

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