다음을 통해 공유


코드 커버리지 분석 사용자 지정

기본적으로 코드 검사는 단위 테스트 중에 로드되는 모든 솔루션 어셈블리를 분석합니다. 대부분의 경우 잘 작동하므로 이 기본 동작을 사용하는 것이 좋습니다. 자세한 내용은 코드 커버리지를 사용하여 테스트되는 코드 양을 확인하세요.

코드 검사 결과에서 테스트 코드를 제외하고 애플리케이션 코드만 포함하려면 ExcludeFromCodeCoverageAttribute 특성을 테스트 클래스에 추가합니다.

솔루션에 포함되지 않은 어셈블리를 포함하려면 이러한 어셈블리에 대한 .pdb 파일을 가져와 어셈블리 .dll 파일과 동일한 폴더에 복사합니다.

메모

코드 검사는 Visual Studio Enterprise에서만 사용할 수 있습니다. 명령줄 도구 dotnet-coverage를 사용하여 .NET 코드 검사를 수행할 수도 있습니다.

설정 파일 실행

실행 설정 파일 단위 테스트 도구에서 사용하는 구성 파일입니다. 고급 코드 검사 설정은 .runsettings 파일에 지정됩니다.

코드 검사를 사용자 지정하려면 다음 단계를 수행합니다.

  1. 솔루션에 실행 설정 파일을 추가합니다. 솔루션 탐색기솔루션의 바로 가기 메뉴에서 추가>새 항목선택하고 XML 파일선택합니다. CodeCoverage.runsettings같은 이름으로 파일을 저장합니다.

    모든 항목 템플릿이 표시되지 않으면 모든 템플릿 표시를 선택한 다음 항목 템플릿을 선택합니다.

  2. 이 문서의 끝에 있는 예제 파일의 콘텐츠를 추가한 다음, 다음 섹션에 설명된 대로 필요에 맞게 사용자 지정합니다.

  3. 실행 설정 파일을 선택합니다.

    Visual Studio 2019 버전 16.4부터 프로젝트 루트에서 실행 설정 파일을 자동으로 검색할 수 있습니다. 그렇지 않은 경우 테스트 메뉴에서 실행 설정 구성을 선택한 다음 솔루션 전체 runsettings 파일 선택을 선택합니다. 명령줄에서 테스트를 실행하기 위한 실행 설정 파일을 지정하려면 단위 테스트 구성참조하세요.

    코드 검사 범위 분석을 선택하면 실행 설정 파일에서 구성 정보를 읽습니다.

    테스트를 실행하거나 코드를 업데이트할 때 이전 코드 검사 결과 및 코드 색 지정은 자동으로 숨겨지지 않습니다.

    사용자 지정 설정을 해제하고 켜려면 테스트 메뉴에서 파일을 선택 취소하거나 선택합니다.

    실행 설정 파일을 선택하려면 테스트 메뉴에서 설정 파일 선택을 선택합니다. 명령줄에서 테스트를 실행하기 위한 실행 설정 파일을 지정하려면 단위 테스트 구성참조하세요.

    코드 검사 범위 분석을 선택하면 실행 설정 파일에서 구성 정보를 읽습니다.

    테스트를 실행하거나 코드를 업데이트할 때 이전 코드 검사 결과 및 코드 색 지정은 자동으로 숨겨지지 않습니다.

    사용자 지정 설정을 끄고 켜려면 테스트선택, 실행 설정 구성을 선택하고 파일 이름을 선택 또는 선택 취소합니다.

기호 검색 경로

코드 커버리지에는 어셈블리에 대한 기호 파일(.pdb 파일)이 필요합니다. 솔루션에서 빌드한 어셈블리의 경우 기호 파일은 일반적으로 이진 파일과 함께 존재하며 코드 검사도 자동으로 작동합니다. 경우에 따라 코드 검사 분석에 참조된 어셈블리를 포함할 수 있습니다. 이러한 경우 .pdb 파일이 이진 파일에 인접하지 않을 수 있지만 .runsettings 파일에서 기호 검색 경로를 지정할 수 있습니다.

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

메모

기호 확인은 특히 많은 어셈블리가 있는 원격 파일 위치를 사용하는 경우 시간이 걸릴 수 있습니다. 따라서 .pdb 파일을 이진 파일(.dll.exe) 파일과 동일한 로컬 위치에 복사하는 것이 좋습니다.

어셈블리 및 멤버 포함 또는 제외

코드 검사 분석에서 어셈블리 또는 특정 형식 및 멤버를 포함하거나 제외할 수 있습니다. 포함 섹션이 비어 있거나 생략된 경우 로드되고 연결된 PDB 파일이 있는 모든 어셈블리가 포함됩니다. 어셈블리 또는 멤버가 Exclude 섹션의 절과 일치하는 경우 코드 검사에서 제외됩니다. 제외 섹션은 포함 섹션보다 우선합니다. IncludeExclude모두에 어셈블리가 나열되면 코드 검사에 포함되지 않습니다.

예를 들어 다음 XML은 이름을 지정하여 단일 어셈블리를 제외합니다.

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

다음 예제에서는 단일 어셈블리만 코드 검사에 포함되도록 지정합니다.

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

다음 표에서는 어셈블리와 멤버를 코드 검사에 포함하거나 제외하기 위해 일치시킬 수 있는 다양한 방법을 보여줍니다.

XML 요소 일치하는 항목
ModulePath 어셈블리 이름 또는 파일 경로로 지정된 어셈블리와 일치합니다.
CompanyName 회사 특성을 기준으로 어셈블리를 일치시킵니다.
PublicKeyToken 공개 키 토큰으로 서명된 어셈블리를 일치시킵니다.
근원 요소가 정의된 원본 파일의 경로 이름으로 일치합니다.
속성 지정된 특성이 있는 요소와 일치합니다. 특성의 전체 이름(예: <Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>)을 지정합니다.

CompilerGeneratedAttribute 특성을 제외하면 async, await, yield return및 자동 구현 속성과 같은 언어 기능을 사용하는 코드는 코드 검사 분석에서 제외됩니다. 실제로 생성된 코드를 제외하려면 GeneratedCodeAttribute 특성만 제외합니다.
기능 매개 변수 목록을 포함하여 프로시저, 함수 또는 메서드를 정규화된 이름으로 찾습니다. 정규식사용하여 이름의 일부를 일치시킬 수도 있습니다.

예제:

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

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

코드 커버리지 형식

기본적으로 코드 커버리지는 수집되어 .coverage 파일에 저장됩니다. Xml 및 Cobertura를 비롯한 다른 형식을 사용하여 커버리지를 수집할 수도 있습니다. 다른 형식은 여러 편집기 및 파이프라인에서 유용할 수 있습니다. runsettings 파일 <Format>Cobertura</Format>DataCollector 구성 섹션에 <Format>Xml</Format> 또는 을 추가하여 runsettings에서 이를 활성화할 수 있습니다. 이 형식은 Visual Studio Enterprise의 코드 검사 결과 창에서 볼 수 있습니다.

runsettings 파일에서 지정하거나 매개 변수에 지정하여 명령줄과 다른 형식을 지정할 수도 있습니다. 예를 들어 dotnet 명령줄은 dotnet test --collect:"Code Coverage;Format=Cobertura"사용합니다. vstest의 경우 vstest.console.exe /collect:"Code Coverage;Format=Cobertura"사용합니다. collect 매개 변수는 runsettings에 지정된 형식을 재정의합니다.

정적 및 동적 고유 계측

Visual Studio 2022 버전 17.2에서는 네이티브 이진 파일을 디스크에서 정적으로 계측하는 옵션을 추가했습니다. 이전 버전에서는 메서드를 계측할 수 없었던 동적 계측만 지원했습니다. 정적 네이티브 계측이 더 안정적이며 권장됩니다. 정적 네이티브 계측을 사용하려면 코드 검사 수집이 필요한 모든 네이티브 프로젝트에 대해 /PROFILE 링크 옵션을 사용하도록 설정해야 합니다.

<EnableStaticNativeInstrumentation>True</EnableStaticNativeInstrumentation> 태그 아래에 <CodeCoverage> 추가하여 runsettings에서 네이티브 정적 계측을 사용하도록 설정할 수도 있습니다. 명령줄 시나리오에 이 메서드를 사용합니다.

기본적으로 동적 네이티브 계측은 항상 사용하도록 설정됩니다. 정적 계측과 동적 계측을 모두 사용하는 경우 Visual Studio는 C++ 코드를 정적으로 계측하려고 시도하지만 가능하지 않은 경우(예: /PROFILE 링크 옵션을 사용하도록 설정하지 않은 경우) 동적 계측이 사용됩니다. <EnableDynamicNativeInstrumentation>False</EnableDynamicNativeInstrumentation>아래에 <CodeCoverage> 추가하여 runsettings에서 동적 네이티브 계측을 완전히 사용하지 않도록 설정할 수 있습니다.

정적 네이티브 계측을 사용하도록 설정하면 테스트 실행 전에 네이티브 이진 파일이 디스크에서 계측되고 대체됩니다. 원래 이진 파일은 테스트 실행 후에 복원됩니다. <EnableStaticNativeInstrumentationRestore>False</EnableStaticNativeInstrumentationRestore> 태그 아래에 <CodeCoverage> 추가하여 runsettings에서 원본 파일 복원을 사용하지 않도록 설정할 수 있습니다. 이는 CI 시나리오에서 특히 유용할 수 있습니다.

정적 네이티브 계측을 사용하도록 설정하면 Visual Studio는 테스트 이진 파일이 있는 디렉터리의 모든 네이티브 이진 파일을 검색하고 계측합니다. 이진 파일을 검색해야 하는 추가 디렉터리를 지정할 수 있습니다. 다음 예제에서는 C:\temp과 해당 하위 디렉터리에 있는 모든 네이티브 이진 파일이 Fabrikam.Math.dll로 끝나는 파일을 제외하고 계측되도록 지정합니다.

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

정규식

포함 및 제외 노드는 와일드카드와 동일하지 않은 정규식을 사용합니다. 모든 일치 항목은 대/소문자를 구분하지 않습니다. 몇 가지 예는 다음과 같습니다.

  • .* 모든 문자의 문자열과 일치합니다.

  • \. 문자 "."와 일치합니다.

  • \( \) 괄호 "( )"와 일치합니다.

  • \\은 파일 경로 구분 기호 "\"와 일치합니다.

  • ^ 문자열의 시작 부분과 일치합니다.

  • $ 문자열의 끝과 일치합니다.

다음 XML에서는 정규식을 사용하여 특정 어셈블리를 포함 및 제외하는 방법을 보여 줍니다.

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

다음 XML에서는 정규식을 사용하여 특정 함수를 포함 및 제외하는 방법을 보여 줍니다.

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

경고

정규식에 이스케이프되지 않거나 일치하지 않는 괄호와 같은 오류가 있으면 코드 커버리지 분석이 실행되지 않습니다.

정규식에 대한 자세한 내용은 Visual Studio정규식 사용을 참조하세요.

샘플 .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>
            <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>