Microsoft MVP성태의 닷넷 이야기
Team Foundation Server: 41. 빌드 스크립트에 Code Coverage 추가 [링크 복사], [링크+제목 복사],
조회: 27422
글쓴 사람
정성태 (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분
정성태

... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...
NoWriterDateCnt.TitleFile(s)
1755정성태9/22/201434238오류 유형: 241. Unity Web Player를 설치해도 여전히 설치하라는 화면이 나오는 경우 [4]
1754정성태9/22/201424565VC++: 80. 내 컴퓨터에서 C++ AMP 코드가 실행이 될까요? [1]
1753정성태9/22/201420594오류 유형: 240. Lync로 세미나 참여 시 소리만 들리지 않는 경우 [1]
1752정성태9/21/201441056Windows: 100. 윈도우 8 - RDP 연결을 이용해 VNC처럼 사용자 로그온 화면을 공유하는 방법 [5]
1751정성태9/20/201438912.NET Framework: 464. 프로세스 간 통신 시 소켓 필요 없이 간단하게 Pipe를 열어 통신하는 방법 [1]파일 다운로드1
1750정성태9/20/201423827.NET Framework: 463. PInvoke 호출을 이용한 비동기 파일 작업파일 다운로드1
1749정성태9/20/201423728.NET Framework: 462. 커널 객체를 위한 null DACL 생성 방법파일 다운로드1
1748정성태9/19/201425377개발 환경 구성: 238. [Synergy] 여러 컴퓨터에서 키보드, 마우스 공유
1747정성태9/19/201428385오류 유형: 239. psexec 실행 오류 - The system cannot find the file specified.
1746정성태9/18/201426068.NET Framework: 461. .NET EXE 파일을 닷넷 프레임워크 버전에 상관없이 실행할 수 있을까요? - 두 번째 이야기 [6]파일 다운로드1
1745정성태9/17/201423027개발 환경 구성: 237. 리눅스 Integration Services 버전 업그레이드 하는 방법 [1]
1744정성태9/17/201431030.NET Framework: 460. GetTickCount / GetTickCount64와 0x7FFE0000 주솟값 [4]파일 다운로드1
1743정성태9/16/201420980오류 유형: 238. 설치 오류 - Failed to get size of pseudo bundle
1742정성태8/27/201426945개발 환경 구성: 236. Hyper-V에 설치한 리눅스 VM의 VHD 크기 늘리는 방법 [2]
1741정성태8/26/201421326.NET Framework: 459. GetModuleHandleEx로 알아보는 .NET 메서드의 DLL 모듈 관계파일 다운로드1
1740정성태8/25/201432491.NET Framework: 458. 닷넷 GC가 순환 참조를 해제할 수 있을까요? [2]파일 다운로드1
1739정성태8/24/201426487.NET Framework: 457. 교착상태(Dead-lock) 해결 방법 - Lock Leveling [2]파일 다운로드1
1738정성태8/23/201422040.NET Framework: 456. C# - CAS를 이용한 Lock 래퍼 클래스파일 다운로드1
1737정성태8/20/201419736VS.NET IDE: 93. Visual Studio 2013 동기화 문제
1736정성태8/19/201425563VC++: 79. [부연] CAS Lock 알고리즘은 과연 빠른가? [2]파일 다운로드1
1735정성태8/19/201418148.NET Framework: 455. 닷넷 사용자 정의 예외 클래스의 최소 구현 코드 - 두 번째 이야기
1734정성태8/13/201419801오류 유형: 237. Windows Media Player cannot access the file. The file might be in use, you might not have access to the computer where the file is stored, or your proxy settings might not be correct.
1733정성태8/13/201426326.NET Framework: 454. EmptyWorkingSet Win32 API를 사용하는 C# 예제파일 다운로드1
1732정성태8/13/201434452Windows: 99. INetCache 폴더가 다르게 보이는 이유
1731정성태8/11/201427048개발 환경 구성: 235. 점(.)으로 시작하는 파일명을 탐색기에서 만드는 방법
1730정성태8/11/201422136개발 환경 구성: 234. Royal TS의 터미널(Terminal) 연결에서 한글이 깨지는 현상 해결 방법
... 121  122  123  124  125  126  127  128  129  130  131  [132]  133  134  135  ...