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

1  2  3  4  5  6  7  8  9  10  11  12  [13]  14  15  ...
NoWriterDateCnt.TitleFile(s)
13313정성태4/9/20234019개발 환경 구성: 671. Hyper-V VM에 Turbo C 2.0 설치 [2]
13312정성태4/8/20234067Windows: 244. Win32 - 시간 만료를 갖는 MessageBox 대화창 구현 (개선된 버전)파일 다운로드1
13311정성태4/7/20234541C/C++: 163. Visual Studio 2022 - DirectShow 예제 컴파일(WAV Dest)
13310정성태4/6/20234185C/C++: 162. Visual Studio - /NODEFAULTLIB 옵션 설정 후 수동으로 추가해야 할 library
13309정성태4/5/20234319.NET Framework: 2107. .NET 6+ FileStream의 구조 변화
13308정성태4/4/20234218스크립트: 47. 파이썬의 time.time() 실숫값을 GoLang / C#에서 사용하는 방법
13307정성태4/4/20233970.NET Framework: 2106. C# - .NET Core/5+ 환경의 Windows Forms 응용 프로그램에서 HINSTANCE 구하는 방법
13306정성태4/3/20233759Windows: 243. Win32 - 윈도우(cbWndExtra) 및 윈도우 클래스(cbClsExtra) 저장소 사용 방법
13305정성태4/1/20234145Windows: 242. Win32 - 시간 만료를 갖는 MessageBox 대화창 구현 (쉬운 버전)파일 다운로드1
13304정성태3/31/20234457VS.NET IDE: 181. Visual Studio - C/C++ 프로젝트에 application manifest 적용하는 방법
13303정성태3/30/20233825Windows: 241. 환경 변수 %PATH%에 DLL을 찾는 규칙
13302정성태3/30/20234456Windows: 240. RDP 환경에서 바뀌는 %TEMP% 디렉터리 경로
13301정성태3/29/20234576Windows: 239. C/C++ - Windows 10 Version 1607부터 지원하는 /DEPENDENTLOADFLAG 옵션파일 다운로드1
13300정성태3/28/20234219Windows: 238. Win32 - Modal UI 창에 올바른 Owner(HWND)를 설정해야 하는 이유
13299정성태3/27/20233976Windows: 237. Win32 - 모든 메시지 루프를 탈출하는 WM_QUIT 메시지
13298정성태3/27/20233961Windows: 236. Win32 - MessageBeep 소리가 안 들린다면?
13297정성태3/26/20234619Windows: 235. Win32 - Code Modal과 UI Modal
13296정성태3/25/20233945Windows: 234. IsDialogMessage와 협업하는 WM_GETDLGCODE Win32 메시지 [1]파일 다운로드1
13295정성태3/24/20234230Windows: 233. Win32 - modeless 대화창을 modal처럼 동작하게 만드는 방법파일 다운로드1
13294정성태3/22/20234410.NET Framework: 2105. LargeAddressAware 옵션이 적용된 닷넷 32비트 프로세스의 가용 메모리 - 두 번째
13293정성태3/22/20234478오류 유형: 853. dumpbin - warning LNK4048: Invalid format file; ignored
13292정성태3/21/20234577Windows: 232. C/C++ - 일반 창에도 사용 가능한 IsDialogMessage파일 다운로드1
13291정성태3/20/20234951.NET Framework: 2104. C# Windows Forms - WndProc 재정의와 IMessageFilter 사용 시의 차이점
13290정성태3/19/20234433.NET Framework: 2103. C# - 윈도우에서 기본 제공하는 FindText 대화창 사용법파일 다운로드1
13289정성태3/18/20233655Windows: 231. Win32 - 대화창 템플릿의 2진 리소스를 읽어들여 자식 윈도우를 생성하는 방법파일 다운로드1
13288정성태3/17/20233756Windows: 230. Win32 - 대화창의 DLU 단위를 pixel로 변경하는 방법파일 다운로드1
1  2  3  4  5  6  7  8  9  10  11  12  [13]  14  15  ...