使用 GoogleTest 对 ESP-IDF 组件进行单元测试(基于主机)
Source: Dev.to
请提供您希望翻译的具体文本内容(文章正文、代码块除外),我将按照要求保持原有的 Markdown 格式和技术术语,仅翻译文本部分为简体中文。谢谢!
Introduction
我想要一种方法,在不每次都烧录板子的情况下测试 ESP‑IDF 组件逻辑。
ESP‑IDF 可以为 Linux 目标构建,从而让测试直接在主机上运行。
本指南展示如何使用 GoogleTest 作为测试框架进行设置。示例仓库是 aluigiotomazelli/gtest‑esp‑idf(章节 01_basic_test)。
前置条件
- ESP‑IDF 5.x 已安装并已 source
- Linux 机器或 WSL2
- ESP‑IDF Linux 目标所需的系统软件包
sudo apt install libbsd0 libbsd-dev
如果使用官方 ESP‑IDF Docker 容器(idf-env:latest),这些软件包已经预装。
注意: Ruby 仅在基于 CMock 的 IDF mock 中需要,相关内容将在后面的章节中介绍。
仓库布局
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 进行模拟时会很有用的模式。
将 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 必须在真实的构建阶段运行,而不是依赖扫描阶段
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
FetchContent_MakeAvailable(googletest)
endif()
# 注册为 INTERFACE 库,以便其他组件可以链接 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
该仓库包含 GitHub Actions 工作流,在每次推送时使用官方 ESP‑IDF Docker 容器执行主机测试。无需硬件,因此 CI 在标准的 GitHub runner 上运行。
接下来是什么?
后续章节将涵盖:
- GMock 用于通过模拟对象隔离组件。
- CMock‑based 方法(在 ESP‑IDF 内部使用)用于模拟硬件依赖。
完整的源代码可在 github.com/aluiziotomazelli/gtest-esp-idf 获取。