嵌入式软件单元测试(五)——Google Test在嵌入式Linux上的移植与踩坑记录

发布时间:2026/9/12 12:47:27
嵌入式软件单元测试(五)——Google Test在嵌入式Linux上的移植与踩坑记录 摘要本文基于实际项目经验记录Google TestGTest在嵌入式Linux平台上的完整移植流程涵盖交叉编译环境准备、CMake工具链配置、测试用例编写与运行并总结了pthread链接、C标准版本、sysroot路径、目标板内存、浮点精度及文件系统路径等六个常见踩坑点及解决方案帮助读者在资源受限的嵌入式环境中顺利落地单元测试。1. 引言在嵌入式Linux开发中单元测试往往容易被忽视。一方面嵌入式系统资源受限跑测试似乎是一种“奢侈”另一方面交叉编译环境复杂测试框架的移植常常让人望而却步。然而随着嵌入式软件规模越来越大、逻辑越来越复杂单元测试的重要性愈发凸显。Google Test简称GTest作为业界广泛使用的C测试框架功能强大、社区活跃完全可以在嵌入式Linux上落地。本文基于实际项目经验记录GTest在嵌入式Linux上的移植过程以及过程中遇到的各种坑和解决方案。2. 为什么选择Google Test在嵌入式Linux平台上可选的单元测试框架并不少但Google Test在以下几个方面具有明显优势断言丰富提供大量宏覆盖值比较、字符串比较、异常断言、死亡测试等场景。测试夹具Test Fixture支持SetUp/TearDown机制便于复用测试环境和资源。参数化测试同一组测试逻辑可以针对多组输入数据运行非常适合嵌入式协议解析、算法验证等场景。跨平台官方支持Linux、Windows、macOS且对交叉编译有较好的兼容性。社区活跃文档完善遇到问题容易找到解决方案。当然GTest也有其局限比如对C标准有一定要求、二进制体积偏大等。但在大多数嵌入式Linux场景下这些都不是致命问题。3. 移植前的准备工作3.1 环境确认在开始移植之前需要确认以下信息目标平台的架构ARM、MIPS、RISC-V等交叉编译工具链的路径和版本目标平台的Linux内核版本和C库类型glibc或musl目标平台的C标准库支持情况libstdc或libc3.2 获取Google Test源码Google Test的源码托管在GitHub上可以通过git克隆或直接下载release包。建议使用release版本而非master分支以保证稳定性。以1.12.1版本为例git clone -b v1.12.1 https://github.com/google/googletest.git3.3 交叉编译工具链验证在开始编译GTest之前先确认交叉编译工具链本身可用。以ARM平台为例arm-linux-gnueabihf-g --version arm-linux-gnueabihf-gcc --version同时确认目标板上可以运行最基本的可执行文件避免把工具链问题误判为GTest移植问题。4. 交叉编译Google Test4.1 使用CMake交叉编译Google Test官方推荐使用CMake构建。交叉编译时需要提供一个工具链文件toolchain file告诉CMake编译器、链接器以及目标平台的相关信息。以下是一个针对ARM平台的工具链文件示例set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g) set(CMAKE_FIND_ROOT_PATH /path/to/sysroot) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)然后执行以下命令进行交叉编译mkdir build-arm cd build-arm cmake -DCMAKE_TOOLCHAIN_FILE../toolchain-arm.cmake \ -DCMAKE_INSTALL_PREFIX/path/to/install \ -DBUILD_GMOCKOFF \ .. make -j4 make install这里关闭了GMock的构建因为嵌入式场景下通常只需要GTest核心功能可以减少编译时间和库体积。4.2 编译产物说明编译完成后会生成以下关键产物libgtest.aGTest静态库链接测试程序时使用。libgtest_main.a包含main函数的静态库链接后无需自己编写main函数。头文件目录包括gtest/gtest.h等头文件。在嵌入式项目中通常将这两个静态库和头文件拷贝到项目的third_party目录下统一管理。5. 编写第一个测试用例交叉编译好GTest之后编写一个简单的测试用例验证移植是否成功。以下是一个针对嵌入式常用字符串处理函数的测试示例#include gtest/gtest.h // 被测函数将字符串转换为大写 char* ToUpper(char* str) { char* p str; while (*p) { if (*p a *p z) { *p - 32; } p; } return str; } TEST(ToUpperTest, BasicConversion) { char input[] hello; EXPECT_STREQ(HELLO, ToUpper(input)); } TEST(ToUpperTest, MixedCase) { char input[] Hello123; EXPECT_STREQ(HELLO123, ToUpper(input)); } TEST(ToUpperTest, EmptyString) { char input[] ; EXPECT_STREQ(, ToUpper(input)); }编译命令如下arm-linux-gnueabihf-g -stdc11 \ -I/path/to/gtest/include \ test_to_upper.cpp \ /path/to/gtest/lib/libgtest.a \ /path/to/gtest/lib/libgtest_main.a \ -lpthread \ -o test_to_upper将生成的可执行文件拷贝到目标板上运行如果输出类似以下内容说明移植成功[] Running 3 tests from 1 test suite. [----------] Global test environment set-up. [----------] 3 tests from ToUpperTest [ RUN ] ToUpperTest.BasicConversion [ OK ] ToUpperTest.BasicConversion (0 ms) [ RUN ] ToUpperTest.MixedCase [ OK ] ToUpperTest.MixedCase (0 ms) [ RUN ] ToUpperTest.EmptyString [ OK ] ToUpperTest.EmptyString (0 ms) [----------] 3 tests from ToUpperTest (0 ms total) [----------] Global test environment tear-down [] 3 tests from 1 test suite. (0 ms total) [ PASSED ] 3 tests.6. 踩坑记录6.1 坑一pthread库未链接GTest内部使用了pthread编译时如果忘记加-lpthread会出现类似“undefined reference to pthread_create”的链接错误。解决方案是在链接命令末尾加上-lpthread。如果使用CMake需要在CMakeLists.txt中显式链接Threads::Threads。6.2 坑二C标准版本过低较新版本的GTest要求C11及以上标准。如果编译时报错提示“C11 features not supported”需要在编译命令中加上-stdc11或更高版本。对于老旧的嵌入式工具链如果只支持C98则需要使用旧版本的GTest如1.8.x。6.3 坑三sysroot路径配置错误交叉编译时如果CMAKE_FIND_ROOT_PATH配置错误CMake可能会找到宿主机上的头文件和库导致编译失败或链接到错误的库。务必确保sysroot路径指向目标平台的根文件系统并且头文件和库的搜索路径都限制在sysroot内。6.4 坑四目标板内存不足GTest生成的测试可执行文件体积较大在内存受限的嵌入式设备上可能无法运行。解决方案包括使用-Os优化选项减小体积。只编译需要的测试文件避免链接无关代码。将测试程序放到可写文件系统如tmpfs或SD卡上运行。如果内存实在紧张可以考虑使用更轻量的测试框架如Unity或CppUTest。6.5 坑五浮点运算结果不一致嵌入式平台可能没有硬件浮点单元FPU使用软浮点soft-float时浮点运算结果可能与宿主机不一致导致EXPECT_FLOAT_EQ等断言失败。解决方案是使用EXPECT_NEAR并设置合理的误差范围或者确保编译选项与目标板硬件匹配如-mfloat-abihard或softfp。6.6 坑六文件系统路径问题测试中如果涉及文件读写需要注意目标板上的文件系统路径与宿主机不同。建议在测试中使用相对路径并通过SetUp中创建临时目录的方式隔离测试环境避免污染目标板上的真实文件。7. 在嵌入式项目中的集成实践7.1 目录结构建议建议在项目中单独建立test目录与被测代码分离结构如下project/ ├── src/ # 被测源码 ├── include/ # 公共头文件 ├── test/ # 测试代码 │ ├── CMakeLists.txt │ ├── test_main.cpp │ ├── test_to_upper.cpp │ └── test_protocol.cpp └── third_party/ └── gtest/ # 交叉编译好的GTest库和头文件7.2 CMake集成示例在test目录下的CMakeLists.txt中可以这样组织测试工程cmake_minimum_required(VERSION 3.10) project(unit_tests CXX) set(CMAKE_CXX_STANDARD 11) 引入GTest set(GTEST_ROOT ${CMAKE_SOURCE_DIR}/../third_party/gtest) include_directories(${GTEST_ROOT}/include) link_directories(${GTEST_ROOT}/lib) 收集被测源码排除main函数 file(GLOB SRC_FILES ${CMAKE_SOURCE_DIR}/../src/*.cpp) list(FILTER SRC_FILES EXCLUDE REGEX .*main\.cpp$) 收集测试文件 file(GLOB TEST_FILES ${CMAKE_SOURCE_DIR}/*.cpp) add_executable(unit_tests ${TEST_FILES} ${SRC_FILES}) target_link_libraries(unit_tests gtest gtest_main pthread)7.3 测试运行与结果收集在嵌入式目标板上运行测试时建议将测试结果输出到日志文件便于后续分析./unit_tests --gtest_outputxml:/tmp/test_results.xml生成的XML结果可以拷贝回宿主机与CI系统如Jenkins、GitLab CI集成实现自动化测试。8. 总结Google Test在嵌入式Linux上的移植并不复杂核心在于交叉编译环境的正确配置。本文记录了从工具链准备、交叉编译、编写测试到集成实践的完整流程并总结了六个常见踩坑点。希望这些经验能帮助读者少走弯路让嵌入式软件也能享受到单元测试带来的质量保障。在实际项目中建议尽早引入单元测试并逐步扩大测试覆盖率让测试成为嵌入式软件开发流程中不可或缺的一环。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询