Skip to content

动力学与控制系统测试框架指南 (Testing Framework Guide)

文档状态: Draft / Active 生成日期: 2026-03-27 主要作者: AI (Gemini)


1. 测试架构背景与策略

在本项目(FCC-Dynamics-C)中,我们采用了 “分层测试策略”(Two-Tier Testing Strategy) 来满足两种截然不同的测试需求:

  1. 代码重构与迁移验证(从 src_legacysrc):需要频繁、细粒度的数值和逻辑对比,确保新旧代码在物理和数学层面的严格等价。
  2. 多场景弹道仿真计算(系统级与集成测试):未来需要验证不同条件(标准弹道、零干扰、高空风等)下的全系统表现。

为此,我们引入了以下工具链:

  • Google Test (GTest):作为核心的 C++ 单元测试框架,用于处理底层的函数逻辑测试,特别是其强大的浮点数断言宏(如 EXPECT_NEAR, EXPECT_DOUBLE_EQ)非常适合物理仿真的数值校验。
  • CMake / CTest:作为统一的测试运行调度器。不仅可以管理 GTest 的单元测试,还能直接挂载带有不同配置参数的集成测试可执行文件。

项目通过 CMake 的 FetchContent 模块自动下载并编译 GTest,开发者无需在本地手动安装任何依赖环境


2. 如何编写与配置单元测试 (Google Test)

2.1 编写测试代码 (.cpp)

测试代码统一存放在 Test/ 目录下。以 Test/io_types_test.cpp 为例,一个标准的单元测试如下:

cpp
#include <gtest/gtest.h>
#include "io/types/io_types.h"

// 使用 TEST 宏定义测试用例。第一个参数是测试套件名,第二个参数是测试名。
TEST(IOTypesTest, Constants) {
    io::Constants c;
    c["pi"] = 3.14159;

    // 数值比较 (推荐使用 EXPECT_NEAR 处理浮点误差)
    EXPECT_NEAR(io::get_constant(c, "pi", 0.0), 3.14159, 1e-5);
    
    // 异常断言
    EXPECT_THROW({
        io::get_constant_or_throw(c, "nonexistent");
    }, std::runtime_error);
}
  • 断言建议
    • 比较浮点数绝对优先使用 EXPECT_NEAR(val1, val2, abs_error)EXPECT_DOUBLE_EQ(val1, val2)
    • 异常测试使用 EXPECT_THROW(statement, exception_type)EXPECT_NO_THROW(statement)
    • 不需要手写 main() 函数,CMake 会自动链接 gtest_main

2.2 配置 CMake (Test/CMakeLists.txt)

添加新的测试文件后,需要在 Test/CMakeLists.txt 中注册该测试。以下是标准模板:

cmake
# 1. 声明可执行文件
add_executable(my_new_module_test
    my_new_module_test.cpp
)

# 2. 包含头文件路径
target_include_directories(my_new_module_test PRIVATE
    ${CMAKE_SOURCE_DIR}/src
)

# 3. 链接目标模块库与 GTest (gtest_main 是必须的)
target_link_libraries(my_new_module_test
    my_target_module_lib
    gtest_main
)

# 4. 自动将其注册到 CTest 中
gtest_discover_tests(my_new_module_test)

# 5. [可选] 设定输出路径保持整洁
set_target_properties(my_new_module_test PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Test
)

3. 如何运行测试 (CTest)

CTest 是 CMake 自带的测试执行工具,所有测试都应该通过 ctest 命令运行,不建议直接执行二进制文件或使用旧版的 .sh 脚本

常规运行方式

build/ 目录下:

bash
# 1. 配置并编译项目及测试
cmake ..
make -j$(nproc)

# 2. 运行所有测试并仅在失败时输出详情 (推荐日常使用)
ctest --output-on-failure

进阶 CTest 命令

  • 按名称过滤运行:例如只运行包含 IO 相关的测试。
    bash
    ctest -R IO
  • 多线程并发运行测试(当测试用例增加后可大幅缩短时间):
    bash
    ctest -j4
  • 查看极详细的日志输出(即使测试通过也显示所有标准输出):
    bash
    ctest -V

4. 进阶规划:集成与系统级测试

针对“将来需要运行不同的标准弹道、零干扰以及注入各种干扰的计算”这一需求,架构规划如下:

4.1 数据驱动的单一入口

系统应当摒弃“为每种场景写一个 main 函数”的模式,转而构建一个带有参数解析能力的统一仿真器程序(例如 sim_runner)。

通过命令行参数读取不同配置文件(JSON/YAML):

bash
./sim_runner --config=data/standard_trajectory.json
./sim_runner --config=data/zero_disturbance.json

4.2 CTest 场景挂载

CMakeLists.txt 中,使用 add_test 指令将不同的配置文件运行定义为独立的测试用例:

cmake
# Test/CMakeLists.txt (示例规划)

# 测试标准弹道配置是否能正常执行并返回 0
add_test(NAME Integration_Standard_Trajectory 
         COMMAND sim_runner --config ${CMAKE_SOURCE_DIR}/data/standard.json)

# 测试零干扰配置
add_test(NAME Integration_Zero_Disturbance 
         COMMAND sim_runner --config ${CMAKE_SOURCE_DIR}/data/zero_dist.json)

这样,每次只需执行 ctest,即可全自动验证系统在数十种不同干预/配置下的运行稳定性。如果需要对输出结果(如 CSV)进行落点分析,可配合 Python pytest 脚本调用该程序。