在ros(ros1&c++)中使用gtest进行单元测试

0 概述

介绍了在ROS1程序中,使用gtest框架编写单元测试,和编译,运行的步骤;

仅是可以满足使用的最小集,持续更新ing;

1 编写单元测试

1.1 在CMakeLists.txt中增加catkin_add_gtest编译指令

 1 #############
 2 ## Testing ##
 3 #############
 4  5 set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}")
 6 ## Add gtest based cpp test target and link libraries
 7 catkin_add_gtest(${PROJECT_NAME}_test
 8   test/test.cpp
 9   src/rayz_pcap_parser.cpp
10 )
11 if(TARGET ${PROJECT_NAME}_test)
12   target_link_libraries(${PROJECT_NAME}_test
13     ${catkin_LIBRARIES}
14     glog
15     pcap
16   )
17 endif()

每一条catkin_add_gtest的CMake命令都会生成一个可执行文件;一般来说,你可以像对待CMakeLists.txt文件中的其他add_executable命令一样对待catkin_add_gtest

添加set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}")是为了使用gdb调试测试程序,可以不添加,建议添加;

1.2 在package.xml中增加测试依赖

你应当在软件包的package.xml文件中使用<test_depend>标记,指定单元测试所需的所有依赖项。至少,c++软件包通常依赖gtest,而Python软件包通常依赖python-nose;

所有<build_depend><exec_depend>都被隐含地视为测试依赖项,因此不应重复;

 1   <!-- The *depend tags are used to specify dependencies -->
 2   <!-- Dependencies can be catkin packages or system dependencies -->
 3   <!-- Examples: -->
 4   <!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
 5   <!--   <depend>roscpp</depend> -->
 6   <!--   Note that this is equivalent to the following: -->
 7   <!--   <build_depend>roscpp</build_depend> -->
 8   <!--   <exec_depend>roscpp</exec_depend> -->
 9   <!-- Use build_depend for packages you need at compile time: -->
10   <!--   <build_depend>message_generation</build_depend> -->
11   <!-- Use build_export_depend for packages you need in order to build against this package: -->
12   <!--   <build_export_depend>message_generation</build_export_depend> -->
13   <!-- Use buildtool_depend for build tool packages: -->
14   <!--   <buildtool_depend>catkin</buildtool_depend> -->
15   <!-- Use exec_depend for packages you need at runtime: -->
16   <!--   <exec_depend>message_runtime</exec_depend> -->
17   <!-- Use test_depend for packages you need only for testing: -->
18   <!-- <test_depend>gtest</test_depend> -->
19   <!-- Use doc_depend for packages you need only for building documentation: -->
20   <!--   <doc_depend>doxygen</doc_depend> -->
21   <buildtool_depend>catkin</buildtool_depend>
22   <build_depend>roscpp</build_depend>
23   <build_depend>std_msgs</build_depend>
24   <build_export_depend>roscpp</build_export_depend>
25   <build_export_depend>std_msgs</build_export_depend>
26   <exec_depend>roscpp</exec_depend>
27   <exec_depend>std_msgs</exec_depend>
28   <test_depend>gtest</test_depend>

1.3 编写测试体

你应当使用Google Test框架来编写c++单元测试;

Google测试文档中的入门指南和示例页面很好地解释了基础知识;高级指南和参考表则概述了EXPECTASSERT宏的全套用法;你可以参考http://google.github.io/googletest/reference/assertions.html

TIPS:

Google包装了一系列EXPECT_*ASSERT_*的宏,区别是:

EXPECT_*失败时,测试用例继续往下执行;

ASSERT_*失败时,直接在当前函数中返回,当前函数中ASSERT_*后面的语句将不会执行;

参考:

 1 #include <gtest/gtest.h>
 2  3 #define private public
 4 #define protected public
 5 #include "../src/rayz_pcap_parser.h"
 6 #undef private
 7 #undef protected
 8  9 10 class TestRayzPcapParser: public testing::Test {
11 protected:
12     virtual void SetUp() {
13         // some initialization of testing
14     }
15     virtual void TearDown() {
16     }
17 protected:
18     // some member variance
19 };
20 21 22 TEST_F(TestRayzPcapParser, testOpenPcapFile_1) {
23     auto paser = std::make_shared<RayzPcapParser>();
24     EXPECT_DEATH((void) paser->OpenPcapFile("/tmp/illegal.pcap"), "");
25 }
26 27 TEST_F(TestRayzPcapParser, testGetNextNFrames_2) {
28     auto paser = std::make_shared<RayzPcapParser>();
29     (void) paser->OpenPcapFile("/tmp/demo.pcap");
30 31     bool isEOF = false;
32     const std::map<double, pcl::PointCloud<pcl::PointXYZI>> &timesToClouds = paser->GetNextNFrames(100, isEOF);
33     std::cout << "timesToClouds.size(): " << timesToClouds.size() << "\n";
34     EXPECT_GE(100, timesToClouds.size());
35 }
36 37 38 int main(int argc, char **argv) {
39     testing::InitGoogleTest(&argc, argv);
40     return RUN_ALL_TESTS();
41 }

2 编译单元测试

(cd /tmp/catkin_ws/ && catkin_make --make-args tests)

TIPS:

(cd /tmp/catkin_ws/ && catkin_make)

直接使用catkin_make指令,不会调用CMakeLists.txt中的catkin_add_gtest编译指令;

3 运行单元测试并输出结论

(cd /tmp/catkin_ws/ && catkin_make --make-args test)

4 调试测试程序

4.1 直接执行程序

在“2 编译单元测试”中,打印的日志中已经包含了可执行程序的路径,直接执行程序,如下:

/tmp/catkin_ws/devel/lib/mtuav-sns-pcap-parser/mtuav-sns-pcap-parser_test

你也可以使用rosrun的方式执行程序,如下:

. /tmp/catkin_ws/devel/setup.bash && rosrun mtuav-sns-pcap-parser mtuav-sns-pcap-parser_test

4.2 使用gdb调试程序

如果你在“2 编译单元测试”前,已经按照1.1添加了-g的编译选项,则你可以使用gdb调试程序,如下:

gdb /tmp/catkin_ws/devel/lib/mtuav-sns-pcap-parser/mtuav-sns-pcap-parser_test

添加断点,和打印变量内容,如下:

4.3 有选择的执行测试用例

googletest支持大量命令行选项,用于控制运行哪些测试、运行多少次以及如何格式化测试结果。你可以通过--help标志查看完整的选项列表。最有用的选项之一是过滤要运行的测试。首先,通过--gtest_list_tests标志查看测试列表,如下:

1 /tmp/catkin_ws/devel/lib/mtuav-sns-pcap-parser/mtuav-sns-pcap-parser_test --gtest_list_tests
2 # 或者:
3 . /tmp/catkin_ws/devel/setup.bash && rosrun mtuav-sns-pcap-parser mtuav-sns-pcap-parser_test --gtest_list_tests

 

您可以向--gtest_filter选项传递这些名称中的任何一个,以明确启用(或禁用)特定的测试用例,如下:

/tmp/catkin_ws/devel/lib/mtuav-sns-pcap-parser/mtuav-sns-pcap-parser_test --gtest_filter=TestRayzPcapParser.testOpenPcapFile_1 

你也可以使用通配符,如下:

/tmp/catkin_ws/devel/lib/mtuav-sns-pcap-parser/mtuav-sns-pcap-parser_test --gtest_filter=TestRayzPcapParser.*2
/tmp/catkin_ws/devel/lib/mtuav-sns-pcap-parser/mtuav-sns-pcap-parser_test --gtest_filter=TestRayzPcapParser.*

x 参考文档

x.1 https://answers.ros.org/question/271167/how-to-best-call-catkin_add_gtest-and-add_rostest_gtest-separately/

x.2 https://personalrobotics.cs.washington.edu/software/unit-testing/

x.3 https://github.com/methylDragon/pcl-ros-tutorial/tree/master

x.4 http://wiki.ros.org/pcl_ros

x.5 http://google.github.io/googletest/reference/assertions.html

x.6 https://leooo48.github.io/2018/08/14/gtest/

x.7 https://www.cnblogs.com/coderzh/archive/2009/04/06/1426758.html

 

posted on 2023-09-22 13:19  _bob  阅读(613)  评论(0编辑  收藏  举报