【ROS2】测试

为什么要进行自动化测试?

以下是我们应该进行自动化测试的许多重要原因之一:

  • 您可以更快地对代码进行增量更新。ROS 有数百个包,具有许多相互依赖关系,因此很难预见一个小变化可能引起的问题。如果您的更改通过了单元测试,您可以更有信心地认为您没有引入问题------或者至少问题不是您的错。

  • 您可以更自信地重构代码。通过单元测试可以验证您在重构时没有引入任何错误。这让您摆脱了对变化的恐惧,获得了这种美妙的自由!

  • 它导致更好的代码设计。单元测试迫使你编写代码,使其更容易测试。这通常意味着保持底层函数和框架分离,这是我们设计 ROS 代码的目标之一

  • 他们防止重复出现的错误(错误回归)。为你修复的每个错误编写单元测试是一个好习惯。事实上,在修复错误之前编写单元测试。这将帮助你精确地,甚至是确定性地重现错误,并更精确地理解问题所在。结果,你还将创建一个更好的补丁,然后可以使用回归测试来验证错误是否已修复。这样,如果代码在以后被修改,错误就不会意外地重新引入。这也意味着说服补丁审查员问题已解决,并且贡献质量很高会更容易。

  • 其他人可以更容易地处理你的代码(自动文档形式)。当你进行更改时,很难判断你是否破坏了别人的代码。单元测试是其他开发人员验证其更改的工具。自动测试记录你的编码决策,并自动向其他开发人员传达其违规行为。因此,测试成为你的代码的文档------一种大多数时间不需要阅读的文档,当需要检查时,测试系统会准确指示需要阅读的内容(哪些测试失败)。通过编写自动测试,你使其他贡献者更快。这改善了整个 ROS 项目。

  • 如果我们有自动化单元测试,成为 ROS 的贡献者会容易得多。对于新的外部开发人员来说,向您的组件做出贡献是非常困难的。当他们对代码进行更改时,通常是在盲目操作,依靠大量的猜测。通过提供自动化测试的工具,您可以帮助他们完成任务。他们会立即获得更改的反馈。这样更容易为项目做出贡献,新贡献者也更容易加入。此外,他们的首次贡献质量更高,从而减少了维护人员的工作量。这是双赢的局面!

  • 自动测试简化了维护工作。特别是对于变化较慢的成熟软件包,主要需要更新到新的依赖项,自动测试套件有助于快速确定软件包是否仍然有效。这使得决定软件包是否仍然受支持变得更加容易。

  • 自动测试放大了持续集成的价值。回归测试以及基于正常场景的需求测试,有助于为您的组件提供整体的自动化测试。您的组件在依赖的其他 API 演变过程中得到了更好的测试(CI 服务器将更好、更准确地告诉您代码中出现的问题)。

也许编写测试最重要的好处是测试使你成为一个好公民。测试从长远来看会影响质量。这是许多开源项目中广泛接受的做法。通过编写回归测试,你正在为 ROS 生态系统的长期质量做出贡献。

这一切都是免费的吗?

当然,天下没有免费的午餐。要获得测试的好处,必须进行一些投资。

  • 您需要开发一个测试,这有时可能会很困难或昂贵。有时它也可能并非易事,因为测试应该是自动化的。如果您的测试涉及特殊硬件(不应该:尝试使用模拟、模拟硬件或将测试缩小到较小的软件问题)或需要外部环境,例如人类操作员,事情会变得特别棘手。

  • 回归测试和其他自动测试需要维护。当组件的设计发生变化时,许多测试会失效(例如,它们不再编译,或抛出与 API 设计相关的运行时异常)。这些测试失败不仅是因为重新设计重新引入了错误,还因为它们需要更新到新的设计。偶尔,对于较大的重新设计,旧的回归测试应该被删除。

  • 大量的测试可能需要很长时间才能运行,这会增加持续集成服务器的成本。

可用教程:

  • 从命令行运行 ROS 2 测试

  • 使用 GTest 编写 C++ 基本测试

  • 用 Python 编写基本测试

从命令行运行 ROS 2 测试

构建并运行你的测试

要编译和运行测试,只需从 colcon 运行测试动词 https://colcon.readthedocs.io/en/released/reference/verb/test.html

apache 复制代码
colcon test --ctest-args tests [package_selection_args]

(其中 package_selection_argscolcon 的可选包选择参数,用于限制构建和运行的包)

在测试之前获取工作区应该是不必要的。 colcon test 确保测试在正确的环境中运行,能够访问它们的依赖项等。

检查测试结果

要查看结果,只需从 colcon 运行 test-result 动词。https://colcon.readthedocs.io/en/released/reference/verb/test-result.html

apache 复制代码
colcon test-result --all

要查看确切的失败测试用例,请使用 --verbose 标志:

apache 复制代码
colcon test-result --all --verbose

使用 GDB 调试测试

有关使用 GDB 调试测试的详细指南,请参阅 GDB 教程 https://docs.ros.org/en/jazzy/How-To-Guides/Getting-Backtraces-in-ROS-2.html

使用 GTest 编写 C++ 基本测试

起点:我们假设您已经设置了一个基本的 ament_cmake 包,并且您想要添加一些测试。

在本教程中,我们将使用 gtest。https://google.github.io/googletest/primer.html

程序包设置

源代码

我们将从一个名为 test/tutorial_test.cpp 的文件中的代码开始

cpp 复制代码
#include <gtest/gtest.h>


TEST(package_name, a_first_test)
{
  ASSERT_EQ(4, 2 + 2);
}


int main(int argc, char ** argv)
{
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

package.xml

将以下行添加到 package.xml

xml 复制代码
<test_depend>ament_cmake_gtest</test_depend>

CMakeLists.txt

ruby 复制代码
if(BUILD_TESTING)
  find_package(ament_cmake_gtest REQUIRED)
  ament_add_gtest(${PROJECT_NAME}_tutorial_test test/tutorial_test.cpp)
  target_include_directories(${PROJECT_NAME}_tutorial_test PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
  )
  target_link_libraries(${PROJECT_NAME}_tutorial_test name_of_local_library)
endif()

测试代码被包装在 if/endif 块中,以尽可能避免构建测试。 ament_add_gtest 的功能类似于 add_executable ,因此您需要像往常一样调用 target_include_directoriesament_target_dependenciestarget_link_libraries

运行测试

请参阅有关如何从命令行运行测试 https://docs.ros.org/en/jazzy/Tutorials/Intermediate/Testing/CLI.html 的教程,以获取有关运行测试和检查测试结果的更多信息。

用 Python 编写基本测试

起点:我们假设您已经设置了一个基本的 ament_python 包,并且您想为其添加一些测试。

如果您使用 ament_cmake_python,请参阅 ament_cmake_python 文档以了解如何使测试可发现。测试内容和使用 colcon 的调用保持不变。

程序包设置

setup.py

您的 setup.py 必须在调用 setup(...) 时对 pytest 有测试依赖:

ini 复制代码
tests_require=['pytest'],

测试文件和文件夹

您的测试代码需要放在包根目录中名为 tests 的文件夹中。

任何包含您要运行的测试的文件必须具有模式 test_FOO.py ,其中 FOO 可以替换为任何内容。

示例包布局:
java 复制代码
awesome_ros_package/
  awesome_ros_package/
      __init__.py
      fozzie.py
  package.xml
  setup.cfg
  setup.py
  tests/
      test_init.py
      test_copyright.py
      test_fozzie.py

测试内容

您现在可以尽情编写测试了。关于 pytest 有很多资源,但简而言之,您可以编写带有 test_ 前缀的函数,并包含您想要的任何断言语句。

python 复制代码
def test_math():
    assert 2 + 2 == 5   # This should fail for most mathematical systems

运行测试

请参阅有关如何从命令行运行测试的教程,以获取有关运行测试和检查测试结果的更多信息。

特殊命令

除了标准的 colcon 测试命令外,您还可以使用 --pytest-args 标志从命令行向 pytest 框架指定参数。例如,您可以指定要运行的函数名称。

nginx 复制代码
colcon test --packages-select <name-of-pkg> --pytest-args -k name_of_the_test_function

要在运行测试时查看 pytest 输出,请使用以下标志:

bash 复制代码
colcon test --event-handlers console_cohesion+