GoogleTest를 활용한 ESP-IDF 구성 요소 단위 테스트 (호스트 기반)
Source: Dev.to
번역할 텍스트가 제공되지 않았습니다. 번역을 원하시는 본문을 알려주시면 한국어로 번역해 드리겠습니다.
소개
보드를 매번 플래시하지 않고 ESP‑IDF 구성 요소 로직을 테스트할 방법이 필요했습니다.
ESP‑IDF는 Linux 타깃으로 빌드할 수 있어 테스트를 호스트 머신에서 직접 실행할 수 있습니다.
이 가이드는 테스트 프레임워크로 GoogleTest를 사용하여 설정하는 방법을 보여줍니다. 예제 저장소는 aluigiotomazelli/gtest‑esp‑idf (chapter 01_basic_test)입니다.
사전 요구 사항
- ESP‑IDF 5.x 설치 및 환경 설정
- Linux 머신 또는 WSL2
- ESP‑IDF Linux 타겟에 필요한 시스템 패키지
sudo apt install libbsd0 libbsd-dev
If you use the official ESP‑IDF Docker container (idf-env:latest), the packages are already present.
Note: Ruby는 CMock‑기반 IDF 모크에만 필요하며, 이는 이후 장에서 다룹니다.
저장소 레이아웃
01_basic_test/
├── CMakeLists.txt
├── include/
│ ├── i_sum.hpp # Interface (abstract base class)
│ └── sum.hpp # Concrete class header
├── src/
│ └── sum.cpp # Production code
├── host_test/
│ ├── gtest/ # GTest wrapper component (CMake only)
│ └── test_sum/ # Test project for the Sum class
└── test_apps/ # Hardware verification (not used for host tests)
- **src/**와 **include/**는 프로덕션 코드를 포함합니다.
- **host_test/**는 모든 호스트‑사이드 테스트 코드를 포함합니다; 두 디렉터리는 절대로 섞이지 않습니다.
i_sum.hpp
Sum에 대한 추상 베이스 클래스를 정의합니다. 이 간단한 예제에서는 반드시 필요하지 않지만, 나중에 GMock을 사용해 목(mock)을 만들 때 유용한 패턴을 보여줍니다.
GoogleTest를 컴포넌트로 추가하기
GoogleTest는 ESP‑IDF에 포함되어 있지 않으므로, host_test/gtest/에 위치한 래퍼 컴포넌트를 통해 도입합니다. 래퍼는 CMakeLists.txt 파일 하나만 포함합니다:
# host_test/gtest/CMakeLists.txt
if(IDF_TARGET STREQUAL "linux")
include(FetchContent)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.14.0
)
# FetchContent must run during the real build phase, not the dependency‑scan phase
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
FetchContent_MakeAvailable(googletest)
endif()
# Register as an INTERFACE library so other components can link to GTest/GMock
add_library(gtest INTERFACE)
target_link_libraries(gtest INTERFACE gtest gmock)
target_include_directories(gtest INTERFACE
${googletest_SOURCE_DIR}/include
)
endif()
핵심 포인트
- Linux 전용 가드 – 빌드 대상이
linux일 때만 컴포넌트가 처리됩니다. - FetchContent – 빌드 시점에 GTest를 다운로드합니다; 버전을 업데이트하려면
GIT_TAG만 바꾸면 됩니다. - 빌드 단계 가드 – 초기 의존성 스캔 단계에서 다운로드 시도를 방지합니다.
- INTERFACE 라이브러리 – 래퍼 자체는 아무 것도 컴파일하지 않으며,
REQUIRES하는 프로젝트에 GTest/GMock을 노출하기만 합니다.
테스트 프로젝트 구성
테스트 프로젝트는 host_test/test_sum/에 있습니다. 중요한 CMake 설정:
# host_test/test_sum/CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS
"${CMAKE_CURRENT_LIST_DIR}/../../.." # 레포지토리 루트 (테스트 대상 컴포넌트가 포함된 위치)
"${CMAKE_CURRENT_LIST_DIR}/../gtest" # GTest 래퍼
)
idf_component_register(
SRCS "test_sum.cpp"
INCLUDE_DIRS "."
REQUIRES sum gtest
WHOLE_ARCHIVE # 정적 생성자(GoogleTest 등록)가 제거되지 않도록 보장
)
- EXTRA_COMPONENT_DIRS는 테스트 대상 컴포넌트(
sum)와 GTest 래퍼를 가리키며, 두 경로는 테스트 프로젝트 폴더 밖에 있습니다. - REQUIRES는 필요한 컴포넌트만 나열하여 빌드 속도를 빠르게 유지합니다.
- WHOLE_ARCHIVE는 모든 오브젝트 파일을 포함하도록 강제하여 GoogleTest가 테스트를 탐지할 수 있게 합니다.
예제 테스트 코드
// host_test/test_sum/test_sum.cpp
#include
#include "sum.hpp"
TEST(TestSum, GTestSmokeTest) {
EXPECT_TRUE(true); // verifies that GoogleTest itself runs
}
TEST(TestSum, Add) {
Sum s;
EXPECT_EQ(s.add(2, 3), 5);
EXPECT_EQ(s.add(-1, -4), -5);
EXPECT_EQ(s.add(0, 0), 0);
}
TEST(TestSum, AddConstrained_InRange) {
Sum s;
EXPECT_EQ(s.add_constrained(3, 4), 7); // 7 ≤ 10
}
TEST(TestSum, AddConstrained_AtLimit) {
Sum s;
EXPECT_EQ(s.add_constrained(5, 5), 10); // exactly the limit
}
TEST(TestSum, AddConstrained_OutOfRange) {
Sum s;
EXPECT_EQ(s.add_constrained(6, 5), -1); // 11 > 10 → -1
}
호스트 테스트 빌드 및 실행
cd 01_basic_test/host_test/test_sum
idf.py --preview set-target linux # Linux target is still experimental
idf.py build
./build/test_sum.elf
예상 출력
[==========] Running 6 tests from 1 test suite.
[----------] 6 tests from TestSum
[ RUN ] TestSum.GTestSmokeTest
[ OK ] TestSum.GTestSmokeTest (0 ms)
...
[ PASSED ] 6 tests.
Continuous Integration
레포지토리에는 공식 ESP‑IDF Docker 컨테이너를 사용하여 푸시마다 호스트 테스트를 실행하는 GitHub Actions 워크플로가 포함되어 있습니다. 하드웨어가 필요 없으므로 CI는 표준 GitHub 러너에서 실행됩니다.
다음은 무엇인가요?
- GMock를 사용하여 모의 객체를 통해 구성 요소를 격리합니다.
- CMock‑based 접근 방식(ESP‑IDF 내부에서 사용)으로 하드웨어 의존성을 모킹합니다.
전체 소스 코드는 **github.com/aluiziotomazelli/gtest-esp-idf**에서 확인할 수 있습니다.