A lightweight wrapper around the Catch2 testing framework for use with ROS 2.
This can be used for unit testing of ROS 2 related functionality or for integration tests to test the functionality of nodes in C++.
To install Debian packages of catch_ros2
simply run the following command (assuming your environment is properly set up for installing Debian packages):
apt install ros-${ROS_DISTRO}-catch-ros2
Debian packages are currently available for Humble, Iron, Jazzy, and Rolling ROS 2 distributions.
To build the package from source, clone it into the src
directory of your workspace root directory (ws
). Then from ws
use colcon build
, like any other ROS 2 package.
See the examples section for guides on usage. This README provides an overview, but more details can be found in the links to example source code.
There are two basic ways to use this framework:
You can also customize further if you need further control.
Integration testing is an important way to ensure different elements of your system work together as desired. In ROS, we generally want to test that our nodes behave correctly, which may difficult with typical unit testing.
ROS 2 already contains a framework for writing integration tests in Python which may be more than sufficient for most users. GTest can also be used to test C++ code within an integration test. However, to my knowledge, I have not seen an easy way to create integration tests in C++ with Catch2 - a simple to use but powerful C++ testing framework. The primary motivation of this package is to provide a framework for just that.
For a launch integration test in this framework you need 4 components:
These are the nodes in your package whose functionality you'd like to test. Nothing special has to be done with these nodes to test them with this framework. This example has integration_aux_node
as the node under test.
This node is specifically written with Catch2 test cases and assertions to test the functionality of other nodes. The example has integration_test_node
as the testing node.
If it is linked with catch_ros2::catch_ros2_with_node_main
(see CMake setup) it does not require a main
function and can be run just like any other ROS 2 node (ros2 run ${package_name} ${executable_name}
). Tests can be executed manually by just running the node like this (along with other necessary nodes).
If more precise functionality beyond the default node main of this package is desired, see selecting a main function.
To automate the integration test, a Python launch file can be used to launch all the necessary nodes for the test, including the test node. An integration test can also be run manually by simply launching this launch file.
This launch file is the same as any other ROS 2 Python launch file, but the launch_catch_ros2
Python module provides a few useful classes:
Catch2LaunchDescription
- a wrapper around the typicallaunch.LaunchDescription
that includes a "result_file" argument where the results of the test will be output.Catch2IntegrationTestNode
- a wrapper around the typicallaunch_ros.actions.Node
that passes the "result_file" argument to Catch2 (via the defaultcatch_ros2
node main function) and shuts down all nodes on exit. This should be used to launch the testing node. Only one should be used per integration test.
XML and YAML launch files can also be used - see the provided links for examples of the above same launch file implemented in those formats.
Most of the CMake required for this integration test is pretty standard for a ROS 2 package (though the ROS 2 boilerplate CMake is not shown in this example). Nodes are built/installed and the launch file is installed to the package's share directory.
A few items are of note:
target_link_libraries(integration_test_node
catch_ros2::catch_ros2_with_node_main
)
This links the integration test node with the version of catch_ros2
that includes the default main for running test nodes. Only one version of the catch_ros2
library need be linked to a target, depending on the need for a default main. See selecting a main function.
catch_ros2_add_integration_test(ExampleIntegration_Test
LAUNCH_FILE example_integration_test.launch.py
)
This custom CMake function is only necessary if the test should be run automatically whenever colcon test
is run. See here for documentation on the function.
Unit testing is a good way to ensure specific elements of your package work well on their own. It's important to ensure functionality is preserved in an ever-evolving codebase.
This package can also be used for unit testing with Catch2. Using Catch2 for unit testing of ROS 2 components is much more straightforward, and you may not need catch_ros2
. However this package provides a few utilities and a newer version of Catch2 than Ubuntu does.
Only 2 components are needed to create a unit test with this framework:
The source file for unit tests can be written like any other Catch2 unit test.
The CMake setup for unit testing is pretty standard for adding a Catch2 test. The only thing of note is linking the source file with catch_ros2::catch_ros2_with_main
, which gives access to catch_ros2
utilities and uses Catch2's default main function. See selecting a main function.
target_link_libraries(example_unit_test
catch_ros2::catch_ros2_with_main
)
catch_ros2
provides some utility functions for unit testing in ROS 2.
- Arguments - utilities for handling command line arguments.
SimulateArgs
- allows you to pass in a string or vector of strings representing command line arguments to create simulatedargc
andargv
values to pass into functions orrclcpp::init()
.SplitROSArgs
- removes ROS arguments from an inputargc
andargv
, so these arguments won't cause problems for other functions (like Catch2 sessions).- See here for an example.
tokenize
- splits a command line string into a vector of strings, ignoring whitespace in quotation marks.- See here for an example.
There are three options for a main function when using catch_ros2
. Which option is selected depends on which library you link your target to - you should only link to one.
This test node main function (defined here) is good for writing ROS 2 test nodes that use Catch2 (ex: for integration tests). It initializes the ROS context with the input arguments, removes the ROS arguments from the input list, runs the Catch session, then shuts down ROS.
You can use this main by linking to catch_ros2::catch_ros2_with_node_main
:
target_link_libraries(${target_name}
catch_ros2::catch_ros2_with_node_main
)
This main function is Catch2's default main function, which is good for typical unit testing scenarios.
You can use this main by linking to catch_ros2::catch_ros2_with_main
:
target_link_libraries(${target_name}
catch_ros2::catch_ros2_with_main
)
If neither of the above two options satisfy your needs, you can define your own custom main function. See Catch2's guidelines on this. See here for an example.
If you want your test to behave like a node, you can use the catch_ros2
node main as a starting point.
To define your own main but still use this library, link to catch_ros2::catch_ros2
:
target_link_libraries(${target_name}
catch_ros2::catch_ros2
)
Currently this repository uses the amalgamated version of Catch2 as a simple way of vendoring v3.5.4 of Catch2. Ubuntu 22.04 currently only provides Catch2 v2, which ROS 2 depends on, so we've made the decision to vendor v3 to be able to use the newest features. In the future, this approach may be abandoned for vendoring Catch2 v3 in its multiple header form.
In order to update the repository for a future release of Catch2 v3, simply copy the amalgamated header and source files from the Catch2 repository and replace those header and source files in this repository. The default main may also need to be updated.
However, since Ubuntu 24.04 provides Catch2 v3.4.0, we may standardize on this version to prevent the need for vendoring v3 for any ROS distros on 24.04. v3 would still be vendored for ROS distros on 22.04. Changes coming soon!
This package is released under the Apache-2.0 License. Catch2 is licensed under the BSL-1.0 License (see their repository for more details).