# 单元测试

我们鼓励开发人员在开发的各个阶段编写单元测试,包括添加新功能、修复错误和重构。

PX4 提供了多种编写单元测试的方法:

  1. 单元测试 谷歌测试 (打开新窗口) ("GTest") - 仅有最小内部依赖关系的测试
  2. 使用 GTest 进行功能测试 - 依赖于参数和 uORB 消息的测试
  3. SITL 单元测试。这适用于需要以完整 SITL 运行的测试。这些测试运行速度更慢,调试也更困难,因此建议尽可能使用 GTest。

# 编写 GTest 单元测试

提示:一般来说,如果您需要访问高级 GTest 实用程序、STL 中的数据结构或需要链接到 参数uorb 库,而应使用功能测试。

创建新单元测试的步骤如下:

  1. 单元测试应分为三个部分:设置、运行、检查结果。每个测试都应测试一个非常具体的行为或设置情况,这样如果测试失败,就能明显看出哪里出了问题。请尽可能遵循这些标准。
  2. 复制并重命名单元测试示例 态度控制测试 (打开新窗口) 到要测试的代码所在的目录。
  3. 将新文件添加到目录的 CMakeLists.txt.它应该看起来像 px4_add_unit_gtest(SRC MyNewUnitTest.cpp LINKLIBS <library_to_be_tested>)
  4. 添加所需的测试功能。这意味着要包含特定测试所需的头文件、添加新测试(每个测试都有独立名称)、设置逻辑、运行要测试的代码并验证其是否符合预期。
  5. 如果需要额外的依赖库,也应将它们添加到 CMakeLists 后的 链接库 如上图所示。

测试可通过 试验之后,您将在 build/px4_sitl_test/unit-MyNewUnit.它可以直接在调试器中运行。

# 编写 GTest 功能测试

当测试或被测组件依赖于参数、uORB 消息和/或高级 GTest 功能时,应使用 GTest 功能测试。此外,功能测试还可包含 STL 数据结构的本地使用(但要注意 macOS 和 Linux 等平台之间的差异)。

创建新功能测试的步骤如下:

  1. 一般来说(与单元测试类似),功能测试应分为三个部分:设置、运行、检查结果。每个测试都应测试一个非常具体的行为或设置情况,这样如果测试失败,就能明显看出问题所在。请尽可能遵循这些标准。
  2. 复制并重命名示例功能测试 参数测试 (打开新窗口) 到要测试的代码所在的目录。
  3. 将 ParameterTest 类重命名为更能代表测试代码的名称
  4. 将新文件添加到目录的 CMakeLists.txt.它应该看起来像 px4_add_functional_gtest(SRC MyNewFunctionalTest.cpp LINKLIBS <library_to_be_tested>)
  5. 添加所需的测试功能。这意味着要包含特定测试所需的头文件、添加新测试(每个测试都有独立名称)、设置测试逻辑、运行要测试的代码并验证其是否符合预期。
  6. 如果需要额外的依赖库,也应将它们添加到 CMakeLists 后的 链接库 如上图所示。

测试可通过 试验之后,您将在 build/px4_sitl_test/functional-MyNewFunctional.它可以直接在调试器中运行,但要注意使用 --gtest_filter=<regex>; (打开新窗口) 参数,因为 uORB 和参数库的某些部分不能完美地自我清理,如果多次设置,可能会导致未定义的行为。

# 编写 SITL 单元测试

当您特别需要飞行控制器的所有组件(驱动程序、时间等)时,应使用 SITL 单元测试。这些测试运行速度较慢(每个新模块需要 1 秒以上),调试起来也比较困难,因此一般只在必要时使用。

创建新 SITL 单元测试的步骤如下:

  1. 检查样本 统一班级 (打开新窗口).

  2. 内创建一个新的 .cpp 文件 试验 (打开新窗口) 用姓名 test_[description].cpp.

  3. test_[description].cpp 包括基础 unittest-class <unit_test.h>; 以及编写新功能测试所需的所有文件。

  4. test_[description].cpp 建班 [说明]测试 继承自 单元测试.

  5. [说明]测试 类声明的公共方法 virtual bool run_tests().

  6. [说明]测试 类声明测试相关功能所需的所有私有方法 (test1(), test2(),...).

  7. test_[description].cpp 实施 run_tests() 方法,每个测试[1,2,...]都将在该方法中运行。

  8. test_[description].cpp实施各种测试。

  9. 在底部内 test_[description].cpp 宣布测试开始。

    ut_declare_test_c(测试[描述], [说明]测试)
    

    这里有一个模板:

    #包括 <unit_test.h>;
    #包括 "[新功能].h";
    ...
     [说明]测试 :  单元测试
    {
    :
        虚拟 bool 运行测试();
    私人:
        bool 测试1();
        bool 测试2();
        ...
    };
    bool [说明]测试::运行测试()
    {
        ut_run_test(测试1)
        ut_run_test(测试2)
        ...
        返回 (测试失败 == 0);
    }
    bool [说明]测试::测试1()
    {
        ut_[单元测试函数之一的名称](...
        ut_[单元测试函数之一的名称](...
        ...
        返回 ;
    }
    bool [说明]测试::测试2()
    {
        ut_[单元测试函数之一的名称](...
        ut_[单元测试函数之一的名称](...
        ...
        返回 ;
    }
    ...
    ut_declare_test_c(测试[描述], [说明]测试)
    

    请注意 ut_[单元测试函数名称] 对应于在 unit_test.h (打开新窗口).

  10. tests_main.h (打开新窗口) 定义新的测试:

    外部 int 测试[描述](int 参数, 烧焦 *参数[]);
    
  11. tests_main.c (打开新窗口) 添加说明名称、测试功能和选项:

    ...
    } 试验[] = {
        {...
        {"[描述]";, 测试[描述], 选项},
        ...
    }
    

    选项 可以是 OPT_NOALLTEST,OPT_NOJIGTEST0 如果在 px4 外壳中调用了这两个命令中的一个,就会被认为是"......":

    pxh>; 全部测试
    

    pxh>; 试验夹具
    

    如果测试有选项 OPT_NOALLTEST则在调用 全部测试.对于 OPT_NOJITEST 当命令 试验夹具 被调用。选项 0 意味着测试永远不会被排除,而这正是大多数开发人员希望使用的。

  12. 添加测试 test_[description].cppCMakeLists.txt (打开新窗口).

# 在本地机器上进行测试

直接从 bash 运行 GTest 单元测试、GTest 功能测试和 SITL 单元测试的完整列表:

生产 试验

单个 GTest 测试二进制文件位于 build/px4_sitl_test/ 目录,并可在大多数集成开发环境的调试器中直接运行。

使用此命令对ctest 名称使用正则表达式进行筛选,以便只运行部分测试:

生产 试验 TESTFILTER=<;regex 过滤器表达式>;

例如

  • make tests TESTFILTER=unit 仅运行 GTest 单元测试
  • make tests TESTFILTER=sitl 仅运行模拟测试
  • make tests TESTFILTER=Attitude 只运行 态度控制 测试