Microsoft MVP성태의 닷넷 이야기
Team Foundation Server: 41. 빌드 스크립트에 Code Coverage 추가 [링크 복사], [링크+제목 복사],
조회: 27391
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 1개 있습니다.)
빌드 스크립트에 Code Coverage 추가

비록 Symbol/Source 서버 구성 및 형상관리 시스템의 도입이 S/W 개발에 도움이 되긴 해도, 품질 향상을 위한 별도의 작업들이 병행되어야 한다는 점에는 이견이 없을 것입니다. Visual Studio 2010의 경우, 다음과 같은 작업들이 IDE 내에서 간편하게 제공이 되는데요.

  • 단위 테스트(Unit Test)
  • 코드 커버리지(Code Coverage)
  • Code Metrics
  • 정적 코드 분석(Static Code Analysis)

이 중에서 "Code Metrics"만 명령행에서 제어가 안됩니다. 즉, LOC(Lines of Code), Cyclomatic Complexity, Class Coupling 등의 관리 정보들이 자동화될 수 없다는 점인데... 잠깐 검사를 해보니, "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies" 폴더의 "MetricsPackage.dll"에 모든 구현이 포함되어 있는데 Visual Studio IDE와 너무 밀착되어 있어서 Reflection으로도 실행하기가 쉽지 않아 보였습니다. (이 때문에 "Code Metrics" 관련 정보만큼은 별도의 다른 도구를 이용하셔야 합니다.)

혹시나, 저처럼 "Code Metrics" 기능이 명령행으로 나오기를 원하신다면, 아래의 connect 사이트에 가서 투표에 동참해주시기 바랍니다. ^^

Expose code metrics via public API and msbuild tasks 
; https://connect.microsoft.com/VisualStudio/feedback/details/497493/expose-code-metrics-via-public-api-and-msbuild-tasks

예전에 VSTS 2008 때에도 위와 같은 요청이 있었는데, 그때는 "Code Metrics" 기능이 제품 개발 말기에 급하게 구현된 것이라서 여력이 없었다는 설명을 본적 있는데,,, VS 2010에서도 구현되지 않았다는 것은 좀 문제가 있어 보입니다. ^^




제목에서와 같이, 이번 작업은 빌드 스크립트에 "코드 커버리지" 기능을 추가하는 것입니다. 이를 위해 지난번에 만들어 둔 빌드 스크립트 및 테스트 프로젝트를 대상으로 이어서 진행합니다.

배치 파일로 팀 빌드 구성
; https://www.sysnet.pe.kr/2/0/905

예제 다운로드
; https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?wid=905&boardid=331301885
(만약 TFS가 없다면, 배치 파일에서 "tf.exe" 실행 부분만 적절하게 제거하시면 됩니다.)

코드 커버리지에 대한 기능 및 그것을 명령행에서 실행하는 방법은 다음에서 자세히 설명해 주고 있습니다.

Code Coverage Basics with Visual Studio Team System
; https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2008/dd299398(v=vs.90)

TN_1203: Code Coverage Basics from the IDE
; http://msdn.microsoft.com/en-us/vstudio/aa718857.aspx

Use code coverage to determine how much code is being tested
; https://learn.microsoft.com/en-us/visualstudio/test/using-code-coverage-to-determine-how-much-code-is-being-tested

빌드 스크립트에 이 기능을 추가하기에 앞서 고려할 것이 있는데요. 바로 "instrumentation"을 위해서 대상 DLL 및 PDB 파일이 변조된다는 점입니다. 이 때문에, 빌드 스크립트에서 심벌 서버에 원본 PDB 파일들을 저장한 후 ~ 코드 서명을 하기 전의 중간 단계에서 바이너리 파일들을 코드 커버리지를 위해서 별도로 복사해 두어야 합니다.

여기서는, "Test"라는 별도의 폴더를 만들어서 보관한다고 가정하고 다음과 같이 스크립트를 추가합니다.

REM ======== Symbol Server

symstore ...[생략]...

REM ======== For Code Coverage
robocopy %SOLUTIONDIR%\bin\%BUILDCONFIG% .\Test

REM ======== Code Signing

signtool ...[생략]...

다음으로, 본격적으로 코드 커버리지를 수행해야 하는데, 이를 위해 "테스트 프로젝트"를 추가합니다. 즉, 여러분들이 만든 코드를 "사용하는 코드"를 만들어 두어야 한다는 것인데요. 이 방법으로는 흔히들 알고 있는 [TestClass], [TestMethod] 등의 특성을 적용한 단위 테스트를 실행할 수도 있고 별도의 EXE를 만들어두고 "Generic test"로 수행하는 방법도 있습니다.

여기서는 "Generic Test"를 이용한 EXE 실행을 해볼 텐데요. 예제 솔루션에 포함된 "TestLibrary"라는 DLL 프로젝트가 코드 커버리지 대상이고, 각각 "CodeCoverageSample1.exe"와 "CodeCoverageSample2.exe"라는 테스트용 EXE 프로젝트를 생성하여 "TestLibrary"를 사용하도록 할 것입니다.

이를 위해, 우선 다음과 같이 "TestLibrary"를 참조하는 2개의 프로젝트를 추가하고,

how_to_integrate_code_coverage_into_buildscript_1.png

빌드 스크립트에서 이 프로젝트들에 대한 빌드를 추가하고, 빌드된 결과물을 ".\Test" 폴더에 복사합니다.

REM ======== Build Test Projects

msbuild %SOLUTIONDIR%\QTesting\CodeCoverageSample1\CodeCoverageSample1.csproj /property:Platform=%TARGET_PLATFORM%;Configuration=%BUILDCONFIG%;
msbuild %SOLUTIONDIR%\QTesting\CodeCoverageSample2\CodeCoverageSample2.csproj /property:Platform=%TARGET_PLATFORM%;Configuration=%BUILDCONFIG%;

robocopy %SOLUTIONDIR%\QTesting\CodeCoverageSample1\bin\%BUILDCONFIG% .\Test
robocopy %SOLUTIONDIR%\QTesting\CodeCoverageSample2\bin\%BUILDCONFIG% .\Test

자, 이제 모든 바이너리들이 준비가 되었으니 코드 커버리지 도구를 명령행에서 수행합니다.

REM ======== Do Code Coverage

vsinstr -coverage .\Test\TestLibrary.dll

Start vsperfmon -coverage -output:TestLibrary.%GLOBAL_VERSION%.coverage

mstest ...[Generic Test를 이용한 CodeCoverageSample1.exe 실행]...
mstest ...[Generic Test를 이용한 CodeCoverageSample2.exe 실행]...

vsperfcmd -shutdown

"vsinstr -coverage" 명령행에 의해 TestLibrary.dll에 코드 커버리지를 위한 IL 코드가 삽입됩니다. 그런 후, vsperfmon.exe를 실행해 둔 상태에서 "mstest.exe"를 이용하여 "Generic Test"를 수행해 주면 정상적으로 코드 커버리지 결과파일이 생성됩니다.

주의할 것은, mstest.exe가 아닌 그냥 대상 exe 파일(예: CodeCoverageSample1.exe)을 직접 수행하는 경우 exe 실행들 간에 코드 커버리지 결과가 병합되지 않고 마지막 실행 파일 기준으로 덮어쓰기가 됩니다.

exe를 감싼 Generic Test를 만드는 방법은 다음의 글에 자세히 소개되어 있습니다.

Visual Studio 2010 Code Coverage Testing in 10 Easy Steps
; http://www.buffington.com/Blog/post/2010/04/27/Visual-Studio-2010-Code-Coverage-Testing-in-10-Easy-Steps.aspx

위의 글에서는 Visual Studio 내부의 테스트 도구에 통합하기 때문에 "*.testsettings" 파일까지 편집하게 되는데, 이 글에서는 명령행에서 직접 실행하는 것이기 때문에 "*.testsettings" 파일까지는 굳이 필요하지 않습니다. 따라서, 단순히 다음과 같이 더미 프로젝트를 하나 만들고 그 안에 "GenericTest1.GenericTest", "GenericTest2.GenericTest"를 만들어 둔 후 각각 CodeCoverageSample1.exe, CodeCoverageSample2.exe를 가리키도록 합니다.

how_to_integrate_code_coverage_into_buildscript_2.png

사실, "*.GenericTest" 파일들은 단순 XML 텍스트 파일이기 때문에 빌드 과정도 필요없습니다. (Generic Test 규격에 맞는 XML 파일을 요구되는 시점에 임의로 생성해서 사용해도 무방합니다.)

이렇게 해서 빌드 스크립트를 확장하면,

REM ======== Do Code Coverage

vsinstr ...

Start ...

mstest /testcontainer:%SOLUTIONDIR%\QTesting\GenericTest1.CodeCoverageSample1\GenericTest1.GenericTest
mstest /testcontainer:%SOLUTIONDIR%\QTesting\GenericTest1.CodeCoverageSample1\GenericTest2.GenericTest

vsperfcmd ...

자... 이렇게 해서, build.bat 파일을 실행하면 "TestLibrary.1.0.86.0.coverage"와 같은 식으로 결과 파일이 생성됩니다. 아쉽게도 이 파일은 바이너리 유형인데다, 단일 파일로는 의미가 없고 반드시 "코드 커버리지" 대상 빌드 결과물인 PDB/DLL/EXE 등의 파일들이 함께 있어야 정상적으로 Visual Studio IDE 내에서 읽혀져 코드 추적이 가능해집니다.

이런 식이라면, "자동화" 한 것에 대한 노력의 가치가 좀 떨어지죠. 이 때문에, 명령행에서 "*.coverage" 파일들에 대한 내용을 해석 가능한 XML 파일로 변환해 주는 도구가 필요한데, 다음의 글에서 배포되는 "CodeCoverageExporter" 프로젝트가 그래서 필요하게 됩니다.

Visual Studio 2010 - Code Coverage 결과를 외부 XML 파일로 출력하는 명령행 도구 제작
; https://www.sysnet.pe.kr/2/0/925

그럼, 마지막 마무리를 위해 다음과 같이 추가해 주면 완벽해지겠군요. ^^

REM ======== Do Code Coverage

vsinstr ...

Start ...

mstest ...
mstest ...

vsperfcmd ...

CodeCoverageExporter.exe .\Test .\Test TestLibrary.%GLOBAL_VERSION%.coverage TestLibrary.%GLOBAL_VERSION%.coverage.xml




한 가지 알아둬야 할 사항이 있는데요. 코드 커버리지를 위한 테스트 실행 파일의 x86/x64 빌드에 따라서 vsperfcmd.exe도 그에 알맞는 버전으로 실행해 줘야 합니다. 만약 이것이 맞지 않게 되면 "*.coverage" 파일이 생성만 될 뿐 내용은 없습니다.

vsperfcmd.exe는 x86/x64에 따라 다음과 같이 구분되어 제공됩니다.

x86: C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Performance Tools
x64: C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Performance Tools\x64

참고로, x64용 vsperfcmd.exe는 Visual Studio 2010에서 새로 추가된 기능이죠. ^^

Visual Studio 2010 x64 code coverage
; http://blogs.msdn.com/b/hilliao/archive/2010/06/18/visual-studio-2010-x64-code-coverage.aspx

여기까지의 과정을 실습한 프로젝트 및 빌드 스크립트 배치 파일을 첨부합니다. (vsperfcmd.exe가 x64용으로 잡혀 있습니다.)



[이 토픽에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 6/28/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2014-07-28 05시12분
정성태

... 106  107  108  109  110  111  112  113  114  115  116  117  118  [119]  120  ...
NoWriterDateCnt.TitleFile(s)
10949정성태4/28/201619884.NET Framework: 575. SharedDomain과 JIT 컴파일파일 다운로드1
10948정성태4/28/201623829.NET Framework: 574. .NET - 눈으로 확인하는 SharedDomain의 동작 방식 [3]파일 다운로드1
10947정성태4/27/201621709.NET Framework: 573. .NET CLR4 보안 모델 - 4. CLR4 보안 모델에서의 조건부 APTCA 역할파일 다운로드1
10946정성태4/26/201624518VS.NET IDE: 106. Visual Studio 2015 확장 - INI 파일을 위한 사용자 정의 포맷 기능 (Syntax Highlighting)파일 다운로드1
10945정성태4/26/201618286오류 유형: 327. VSIX 프로젝트 빌드 시 The "VsTemplatePaths" task could not be loaded from the assembly 오류 발생
10944정성태4/22/201619519디버깅 기술: 80. windbg - 풀 덤프 파일로부터 텍스트 파일의 내용을 찾는 방법
10943정성태4/22/201624378디버깅 기술: 79. windbg - 풀 덤프 파일로부터 .NET DLL을 추출/저장하는 방법 [1]
10942정성태4/19/201619681디버깅 기술: 78. windbg 사례 - .NET 예외가 발생한 시점의 오류 분석 [1]
10941정성태4/19/201619588오류 유형: 326. Error MSB8020 - The build tools for v120_xp (Platform Toolset = 'v120_xp') cannot be found.
10940정성태4/18/201622865Windows: 116. 프로세스 풀 덤프 시간을 줄여 주는 Process Reflection [3]
10939정성태4/18/201623888.NET Framework: 572. .NET APM 비동기 호출의 Begin...과 End... 조합 [3]파일 다운로드1
10938정성태4/13/201623455오류 유형: 325. 파일 삭제 시 오류 - Error 0x80070091: The directory is not empty.
10937정성태4/13/201631674Windows: 115. UEFI 모드로 윈도우 10 설치 가능한 USB 디스크 만드는 방법
10936정성태4/8/201642363Windows: 114. 삼성 센스 크로노스 7 노트북의 운영체제를 USB 디스크로 새로 설치하는 방법 [3]
10935정성태4/7/201626658웹: 32. Edge에서 Google Docs 문서 편집 시 한영 전환키가 동작 안하는 문제
10934정성태4/5/201625380디버깅 기술: 77. windbg의 콜스택 함수 인자를 쉽게 확인하는 방법 [1]
10933정성태4/5/201630996.NET Framework: 571. C# - 스레드 선호도(Thread Affinity) 지정하는 방법 [8]파일 다운로드1
10932정성태4/4/201623287VC++: 96. C/C++ 식 평가 - printf("%d %d %d\n", a, a++, a);
10931정성태3/31/201623561개발 환경 구성: 283. Hyper-V 내에 구성한 Active Directory 환경의 시간 구성 방법 [3]
10930정성태3/30/201621515.NET Framework: 570. .NET 4.5부터 추가된 CLR Profiler의 실행 시 Rejit 기능
10929정성태3/29/201631628.NET Framework: 569. ServicePointManager.DefaultConnectionLimit의 역할파일 다운로드1
10928정성태3/28/201637340.NET Framework: 568. ODP.NET의 완전한 닷넷 버전 Oracle ODP.NET, Managed Driver [2]파일 다운로드1
10927정성태3/25/201626548.NET Framework: 567. System.Net.ServicePointManager의 DefaultConnectionLimit 속성 설명
10926정성태3/24/201626087.NET Framework: 566. openssl의 PKCS#1 PEM 개인키 파일을 .NET RSACryptoServiceProvider에서 사용하는 방법 [10]파일 다운로드1
10925정성태3/24/201620387.NET Framework: 565. C# - Rabin-Miller 소수 생성 방법을 이용하여 RSACryptoServiceProvider의 개인키를 직접 채워보자 - 두 번째 이야기파일 다운로드1
10924정성태3/22/201621046오류 유형: 324. Visual Studio에서 Azure 클라우드 서비스 생성 시 Failed to initialize the PowerShell host 에러 발생
... 106  107  108  109  110  111  112  113  114  115  116  117  118  [119]  120  ...