본문 바로가기

개발

CI 입문 ㅡ Jenkins로 C++ 빌드·유닛 테스트 자동화 따라하기

들어가며

최근 빌드 환경 구성이 잦아지면서 적지 않은 스트레스를 겪고 있다.

 

현재 진행 중인 프로젝트는 VirtualBox로 각자 빌드 환경을 구성하고,

PowerShell 스크립트를 이용해 Windows, Linux, Embedded Linux 빌드를 각각 수행한다.

 

오래전부터 많이 사용해 온 방식이고, 간단하다는 장점도 있지만

환경이 늘어날수록 관리 부담이 커지는 한계도 느끼게 됐다.

 

더 나은 방법이 없을까 고민하던 중,

예전부터 막연히 갖고 있던 빌드 자동화와 Unit Test 자동화에 대한 욕심이 생겼다.

 

이 글은 Jenkins를 이용한 자동화 환경을 직접 구성해 보기 위해 정리한 기록이다.

gtest + MinGW + Jenkins 조합으로 최소 단위의 자동화를 실험해 보았다.


Jenkins 선택 이유

여러 CI 도구 중 Jenkins를 선택한 이유는,

사내 환경에서 비교적 자유롭게 구성할 수 있고

Windows 기반 빌드에도 익숙하게 대응할 수 있기 때문이다.


gtest 구성

유닛 테스트 환경은 예전에 디자인 패턴을 공부하며 사용했던 코드를 활용했다.

https://github.com/BlueBird-0/cpp-designpattern-example

 

GitHub - BlueBird-0/cpp-designpattern-example: Example of a design pattern written in C++.

Example of a design pattern written in C++. Contribute to BlueBird-0/cpp-designpattern-example development by creating an account on GitHub.

github.com

 

해당 저장소를 Git으로 checkout 한 뒤, CMake 기반으로 빌드를 진행했다.

 

프로젝트의 전체 폴더 구조는 다음과 같다.

C:\Projects
┗ cpp-designpattern
    ┣ CMakeLists.txt // 내부폴더의 모든 테스트를 진행
    ┣ AdapterTest
    ┃  ┣ CMakeLists:txt //AdapterTest 만 따로 빌드할 경우 사용
    ┃  ┗ AdapterTest.cpp
    ┣ BridgeTest 
    ┃  ┣ CMakeLists:txt //BridgeTest 만 따로 빌드할 경우 사용
    ┃  ┗ BridgeTest.cpp
    ┣ ...

루트 디렉터리의 CMakeLists.txt 는 하위 테스트들을 모두 포함하도록 구성되어 있으며,

이를 통해 전체 테스트를 한 번에 빌드하고 실행할 수 있다.


googletest 결과 레포트 생성

통합 빌드를 완료한 뒤, CMake에서 제공하는 ctest 기능을 이용해

googletest 결과를 JUnit 형식의 레포트로 출력했다.

ctest --output-junit test_result.xml

--output-junit 옵션을 사용하면 테스트 결과가 XML 파일로 생성되며,

이 파일은 Jenkins와 같은 CI 도구에서 바로 활용할 수 있다.

 

실행 결과는 다음과 같다.

더보기
PS C:\Projects\cpp-designpattern-example\build\mingw-debug> ctest --output-junit test_result.xml
Test project C:/Projects/cpp-designpattern-example/build/mingw-debug
      Start  1: AdapterTest
 1/17 Test  #1: AdapterTest ......................   Passed    0.01 sec
      Start  2: BridgeTest
 2/17 Test  #2: BridgeTest .......................   Passed    0.01 sec
      Start  3: ChainOfResponsibilityTest
 3/17 Test  #3: ChainOfResponsibilityTest ........   Passed    0.01 sec
      Start  4: CommandTest
 4/17 Test  #4: CommandTest ......................   Passed    0.01 sec
      Start  5: CompositeTest
 5/17 Test  #5: CompositeTest ....................   Passed    0.01 sec
      Start  6: DecoratorTest
 6/17 Test  #6: DecoratorTest ....................   Passed    0.01 sec
      Start  7: FacadeTest
 7/17 Test  #7: FacadeTest .......................   Passed    0.01 sec
      Start  8: FactoryMethodTest
 8/17 Test  #8: FactoryMethodTest ................   Passed    0.01 sec
      Start  9: FlyweightTest
 9/17 Test  #9: FlyweightTest ....................   Passed    0.01 sec
      Start 10: InterpreterTest
10/17 Test #10: InterpreterTest ..................   Passed    0.01 sec
      Start 11: MediatorTest
11/17 Test #11: MediatorTest .....................   Passed    0.01 sec
      Start 12: MementoTest
12/17 Test #12: MementoTest ......................   Passed    0.01 sec
      Start 13: ObserverTest
13/17 Test #13: ObserverTest .....................   Passed    0.01 sec
      Start 14: PrototypeTest
14/17 Test #14: PrototypeTest ....................   Passed    0.01 sec
      Start 15: ProxyTest
15/17 Test #15: ProxyTest ........................   Passed    0.01 sec
      Start 16: StateTest
16/17 Test #16: StateTest ........................   Passed    0.01 sec
      Start 17: StrategyTest
17/17 Test #17: StrategyTest .....................   Passed    0.01 sec

100% tests passed, 0 tests failed out of 17

Total Test time (real) =   0.20 sec

모든 테스트가 정상적으로 통과했고, 자동화를 위한 최소 단위 테스트 환경이 준비되었다.


Jenkins 환경 구성

이제 위 과정을 자동화하기 위해 Jenkins를 설치했다.

http://localhost:8080/

Jenkins는 웹 기반 UI를 제공해, 비교적 쉽게 Job을 생성할 수 있었다.

이번 테스트에서는 Freestyle Project를 사용해 간단한 자동화를 구성했다.

설치된 Jenkins Mainpage


Jenkins 빌드 스크립트

Build Steps 의 Execute Windows batch command 항목에

아래와 같은 빌드 스크립트를 작성했다.

@echo off
chcp 65001 > nul

REM ===== Tool Path =====
set CMAKE_EXE=C:\Qt\Tools\CMake_64\bin\cmake.exe

REM ===== Local Project Path (임시) =====
set PROJECT_DIR=C:\Projects\cpp-designpattern-example
set BUILD_DIR=%PROJECT_DIR%\build\mingw-debug

REM ===== Clean =====
"%CMAKE_EXE%" --build "%BUILD_DIR%" --target clean
if errorlevel 1 exit /b 1

REM ===== Build =====
"%CMAKE_EXE%" --build "%BUILD_DIR%" --parallel
if errorlevel 1 exit /b 1

REM ===== Run gtest (or ctest if 전환했다면) =====
cd /d "%BUILD_DIR%"
ctest --output-junit test_result.xml
if errorlevel 1 exit /b 1

REM ===== Copy report to workspace =====
copy /Y C:\Projects\cpp-designpattern-example\build\mingw-debug\test_result.xml %WORKSPACE%\test_result.xml

빌드 → 테스트 → 레포트 생성 → Workspace 복사까지를 하나의 Job으로 묶었다.

 

이후 빌드 후 조치(Post-build Actions)에서

Publish JUnit test result report를 설정해 레포트 파일(test_result.xml)을 Jenkins에 연결했다.

Jenkins를 통한 빌드 후 Console Output
Jenkins로 보고된 레포트 xml파일


회고

Jenkins 툴은 걱정했던 것보다 훨씬 다루기 쉬운 도구였다.

 

Local PC 환경을 그대로 사용하면서도

빌드와 테스트를 자동화할 수 있다는 점에서

실무 프로젝트에 충분히 도입 가능하다는 확신을 얻었다.

 

살짝 공부한 바로는 Jenkins는 기본적으로 Workspace 내부에서 빌드 환경을 관리하는 구조를 갖고 있는데,

이번 포스팅에서는 Local 자원을 활용하면서 Jenkins가 제공하는 자동화와 기록의 강력함을 체감할 수 있었다.

 

특히 테스트 결과를 레포트로 남기고, 히스토리로 관리할 수 있다는 점에서

팀 단위 CI / Unit Test 자동화의 필요성에 대해 다시 한번 생각하게 됐다.