在ros(ros1&c++)中使用gtest进行单元测试
介绍了在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测试文档中的入门指南和示例页面很好地解释了基础知识;高级指南和参考表则概述了EXPECT
和ASSERT
宏的全套用法;你可以参考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>> ×ToClouds = 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.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