Microsoft MVP성태의 닷넷 이야기
Team Foundation Server: 41. 빌드 스크립트에 Code Coverage 추가 [링크 복사], [링크+제목 복사],
조회: 27432
글쓴 사람
정성태 (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)
11124정성태1/4/201727802개발 환경 구성: 309. 3년짜리 유효 기간을 제공하는 StartSSL [2]
11123정성태1/3/201723314.NET Framework: 629. .NET Core의 dotnet.exe CLI 명령어 확장 방법 [1]
11122정성태1/3/201722776.NET Framework: 628. TransactionScope에 사용자 정의 트랜잭션을 참여시키는 방법 [2]파일 다운로드1
11121정성태1/1/201720673개발 환경 구성: 308. "ASP.NET Core Web Application (.NET Core)"와 "ASP.NET Core Web Application (.NET Framework)" 차이점
11120정성태12/25/201626529개발 환경 구성: 307. ASP.NET Core Web Application을 IIS에서 호스팅하는 방법
11119정성태12/23/201649175개발 환경 구성: 306. Visual Studio Code에서 Python 개발 환경 구성 [2]
11118정성태12/22/201635955오류 유형: 374. Python 64비트 설치 시 0x80070659 오류 발생 [3]
11117정성태12/21/201622263웹: 35. nopCommerce 예제 사이트 구성 방법
11116정성태12/21/201624199디버깅 기술: 84. NopCommerce의 Autofac 부하(CPU, Memory) [2]
11115정성태12/21/201627181Windows: 133. 윈도우 서버 2016에서 플래시가 동작하지 않는 경우 [2]
11114정성태12/19/201637189Windows: 132. 역슬래시(backslash) 문자가 왜 통화 표기 문자(한글인 경우 "\")로 보일까요? [2]
11113정성태12/6/201621086오류 유형: 373. ICOMAdminCatalog::GetCollection에서 CO_E_ISOLEVELMISMATCH(0x8004E02F) 오류 발생파일 다운로드1
11112정성태11/23/201626278오류 유형: 372. MySQL 서비스가 올라오지 않는 경우 - Error 1067
11111정성태11/23/201634751.NET Framework: 627. C++로 만든 DLL을 C#에서 사용하기 [2]
11110정성태11/17/201621427.NET Framework: 626. Commit 메모리가 낮은 상황에서도 메모리 부족(Out-of-memory) 예외 발생 [2]
11109정성태11/17/201621383.NET Framework: 625. ASP.NET에서 System.Web.HttpApplication 인스턴스는 다중으로 생성됩니다.
11108정성태11/13/201621225.NET Framework: 624. WPF - Line 요소를 Canvas에 위치시켰을 때 흐림(blur) 현상파일 다운로드1
11107정성태11/9/201625114오류 유형: 371. Post cache substitution is not compatible with modules in the IIS integrated pipeline that modify the response buffers.파일 다운로드1
11106정성태11/8/201625277.NET Framework: 623. C# - PeerFinder를 이용한 Wi-Fi Direct 데이터 통신 예제 [2]파일 다운로드1
11105정성태11/8/201619667.NET Framework: 622. PeerFinder Wi-Fi Direct 통신 시 Read/Write/Dispose 문제
11104정성태11/8/201619148개발 환경 구성: 305. PeerFinder로 Wi-Fi Direct 연결 시 방화벽 문제
11103정성태11/8/201619113오류 유형: 370. PeerFinder.ConnectAsync의 결과 값인 Task.Result를 호출할 때 System.AggregateException 예외 발생
11102정성태11/8/201619175오류 유형: 369. PeerFinder.FindAllPeersAsync 호출 시 System.UnauthorizedAccessException 예외 발생
11101정성태11/8/201621987.NET Framework: 621. 닷넷 프로파일러의 오류 코드 - 0x80131363
11100정성태11/7/201628742개발 환경 구성: 304. Wi-Fi Direct 지원 여부 확인 방법 [1]
11099정성태11/7/201630709.NET Framework: 620. C#에서 C/C++ 함수로 콜백 함수를 전달하는 예제 코드파일 다운로드1
... 106  107  108  109  110  111  [112]  113  114  115  116  117  118  119  120  ...