做淘客必须有自己内部网站吗,深圳招聘信息在哪个网站,湘潭网站建设哪些公司,廊坊网站制作公司排名前言 在ROS#xff08;Robot Operating System#xff09;中#xff0c;gtest#xff08;Google Test#xff09;是一个广泛使用的C测试框架#xff0c;用于编写和执行单元测试。这些测试可以验证ROS节点、服务和消息等的正确性和性能。 如果我们需要在写的包中添加测试Robot Operating System中gtestGoogle Test是一个广泛使用的C测试框架用于编写和执行单元测试。这些测试可以验证ROS节点、服务和消息等的正确性和性能。 如果我们需要在写的包中添加测试gtest是一个好的选择。在ROS中使用gtest进行单元测试的基本步骤如下
编写测试代码首先我们需要编写使用gtest框架的C测试代码。这些代码通常位于一个单独的ROS包中并包含定义测试用例的TEST宏。配置CMakeLists.txt在我们的ROS包的CMakeLists.txt文件中需要添加必要的gtest依赖项和编译指令。这通常包括find_package(catkin REQUIRED COMPONENTS roscpp rospy gtest)来查找gtest包以及catkin_add_gtest来添加gtest测试目标。构建和运行测试使用ROS的构建系统如catkin或colcon来构建包。构建完成后可以使用rostest或ctest等命令来运行gtest测试。 动动手 如果我们需要在一个基于ament_cmake的功能包中添加单元测试可以借鉴如下方法流程。
功能包配置
源代码 我们从test/tutorial_test.cpp里面的代码开始。
#include gtest/gtest.hTEST(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
test_dependament_cmake_gtest/test_depend
CMakeLists.txt
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)ament_target_dependencies(${PROJECT_NAME}_tutorial_teststd_msgs)target_link_libraries(${PROJECT_NAME}_tutorial_test name_of_local_library)
endif() 我们将测试代码涵括在if/endif语句块中当宏BUILD_TESTING为真时即编译测试模块。其中的ament_add_gtest函数很像add_executable所以我们同样需要调用target_include_directories,ament_target_dependencies和target_link_libraries来包括我们需要的文件。
构建运行 参见上一篇博文。其实在之前创建的ros2_ws工作空间路径下我们已经在此空间下练习了很多例子我们可以直接运行colcon test命令就会将该工作空间下的所有包都执行了次单元测试(如果需要单独测试某个包也可以colcon test --package-select package_name)也可以通过上一篇博文中的colcon test-result --all命令进行查看结果。 在工作空间根路径下的build文件夹下对应的包路径下会生成Testing文件夹里面包含了一些单元测试的结果或记录文件。 本篇稍显粗略不够详实。一个完整的例子可以参考这里将新包放入到工作空间src路径下依次执行如下操作
编译构建
$colcon build --package-select minimal_integration_test
运行测试用例
$colcon test --packages-select minimal_integration_test
具体例子 此例参考了cnblog上面的一篇博文。 我们在前面的教程中已经创建过tutorial_interfaces功能包及cpp_srvcli包我们利用它来实现单元测试用例基于客户端/服务端的服务通信方式。 创建service包 可选 如果之前的cpp_srvcli包还在的话此service包可以不用再创建。 进入工作空间src路径下执行如下命令创建service包
$ros2 pkg create --build-type ament_cmake --license Apache-2.0 service --dependencies rclcpp tutorial_interfaces src/service.cpp 在service包的src路径下创建service.cpp文件内容如下
#include rclcpp/rclcpp.hpp
#include tutorial_interfaces/srv/add_three_ints.hpp // CHANGE#include memoryvoid add(const std::shared_ptrtutorial_interfaces::srv::AddThreeInts::Request request, // CHANGEstd::shared_ptrtutorial_interfaces::srv::AddThreeInts::Response response) // CHANGE
{response-sum request-a request-b request-c; // CHANGERCLCPP_INFO(rclcpp::get_logger(rclcpp), Incoming request\na: %ld b: %ld c: %ld, // CHANGErequest-a, request-b, request-c); // CHANGERCLCPP_INFO(rclcpp::get_logger(rclcpp), sending back response: [%ld], (long int)response-sum);
}int main(int argc, char **argv)
{rclcpp::init(argc, argv);std::shared_ptrrclcpp::Node node rclcpp::Node::make_shared(add_three_ints_server); // CHANGErclcpp::Servicetutorial_interfaces::srv::AddThreeInts::SharedPtr service // CHANGEnode-create_servicetutorial_interfaces::srv::AddThreeInts(add_three_ints, add); // CHANGERCLCPP_INFO(rclcpp::get_logger(rclcpp), Ready to add three ints.); // CHANGErclcpp::spin(node);rclcpp::shutdown();
} 创建client包 进入工作空间根路径src执行如下命令创建client包
$ros2 pkg create --build-type ament_cmake --license Apache-2.0 client --dependencies rclcpp tutorial_interfaces include/client/client.h 在client包的include路径创建client.h,内容如下
// client.h
#ifndef CLIENT_H
#define CLIENT_Hclass ClientHandler
{public:ClientHandler();~ClientHandler();bool sendParams(int argc, char **argv);
};
#endifinclude/client/params.h
// params.h
#ifndef PARAMS_H
#define PARAMS_Hextern int my_argc;
extern char** my_argv;#endifsrc/client.cpp
// client.cpp
#include rclcpp/rclcpp.hpp
#include tutorial_interfaces/srv/add_three_ints.hpp
#include ../include/client/client.h#include chrono
#include cstdlib
#include memory
#includevector
using namespace std;using namespace std::chrono_literals;// 构造函数
ClientHandler::ClientHandler(){}// 析构函数
ClientHandler::~ClientHandler(){}// 普通函数——发送参数
bool ClientHandler::sendParams(int argc, char **argv)
{rclcpp::init(argc, argv);if (argc ! 4) {RCLCPP_INFO(rclcpp::get_logger(rclcpp), usage: add_three_ints_client X Y Z); return false;}std::shared_ptrrclcpp::Node node rclcpp::Node::make_shared(add_three_ints_client); rclcpp::Clienttutorial_interfaces::srv::AddThreeInts::SharedPtr client node-create_clienttutorial_interfaces::srv::AddThreeInts(add_three_ints); auto request std::make_sharedtutorial_interfaces::srv::AddThreeInts::Request(); request-a atoll(argv[1]);request-b atoll(argv[2]);request-c atoll(argv[3]); while (!client-wait_for_service(1s)) {if (!rclcpp::ok()) {RCLCPP_ERROR(rclcpp::get_logger(rclcpp), Interrupted while waiting for the service. Exiting.);return false;}RCLCPP_INFO(rclcpp::get_logger(rclcpp), service not available, waiting again...);}auto result client-async_send_request(request);// Wait for the result.if (rclcpp::spin_until_future_complete(node, result) rclcpp::FutureReturnCode::SUCCESS){RCLCPP_INFO(rclcpp::get_logger(rclcpp), Sum: %ld, result.get()-sum);} else {RCLCPP_ERROR(rclcpp::get_logger(rclcpp), Failed to call service add_three_ints);}rclcpp::shutdown();return true;
}src/main.cpp
// main.cpp
#include ../include/client/client.hint main(int argc, char **argv){// 注意这里: C 编译器把不带参数的构造函数优先认为是一个函数声明ClientHandler client{};client.sendParams(argc, argv);
}单元测试文件
test/clientTest.cpp client包根路径下创建test文件夹。
// clientTest.cpp
#include gtest/gtest.h#include ../include/client/client.h
#include ../include/client/params.hTEST(ClientHandler, sendParams)
{// 测试的时候的交互方式也不能改变既然client实际的效果是在命令行输入参数,// 那这里也是这样的效果ClientHandler client{};EXPECT_EQ(true, client.sendParams(my_argc, my_argv));
}test/main.cpp
// main.cpp
#include gtest/gtest.h
// #include gmock/gmock.hint my_argc;
char** my_argv;int main(int argc, char** argv) {// ::testing::InitGoogleMock(argc, argv);// 注意这里使用的是Gtest不是Gmock::testing::InitGoogleTest(argc, argv); // Runs all tests using Google Test.my_argc argc;my_argv argv;return RUN_ALL_TESTS();
}CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(client)if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES Clang)add_compile_options(-Wall -Wextra -Wpedantic)
endif()# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(tutorial_interfaces REQUIRED)set(SRCsrc/client.cppsrc/main.cpp
)add_executable(client ${SRC})
ament_target_dependencies(clientrclcpp tutorial_interfaces)# 5. 添加当前项目中的头文件 注意有顺序的要求不能乱
target_include_directories(clientPRIVATE ${PROJECT_SOURCE_DIR}/include
)# 如果是测试代码
if(BUILD_TESTING)find_package(ament_lint_auto REQUIRED)# 加入gtest包find_package(ament_cmake_gtest REQUIRED)# the following line skips the linter which checks for copyrights# uncomment the line when a copyright and license is not present in all source files# set(ament_cmake_copyright_FOUND TRUE)# the following line skips cpplint (only works in a git repo)# uncomment the line when this package is not in a git repo# set(ament_cmake_cpplint_FOUND TRUE)set(TESTtest/main.cpptest/clientTest.cpp)# 生成加入gtest的test执行文件。${PROJECT_NAME}_test为自定义的test执行文件名称test/demo_test.cpp为test源码路径# 注意这里导包的时候不再需要将 .h 文件导入进来因为在 client.cpp中已经导入了我们需要使用到的.h文件# 另外注意这里不能导入开发代码中的 main.cpp因为已经有了一个测试的main.cppament_add_gtest(${PROJECT_NAME}_test ${TEST} src/client.cpp)# 务必注意这里需要添加的依赖包ament_target_dependencies(${PROJECT_NAME}_test rclcpp tutorial_interfaces)install(TARGETS${PROJECT_NAME}_test# 将生成的test执行文件安装到DESTINATION后的路径下DESTINATION lib/${PROJECT_NAME}) ament_lint_auto_find_test_dependencies()
endif()install(TARGETSclientDESTINATION lib/${PROJECT_NAME})# 设置编译构建类型为 调试 模式
set(CMAKE_BUILD_TYPE Debug)
# 生成覆盖率文件
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} --coverage)
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} --coverage)ament_package()package.xml
?xml version1.0?
?xml-model hrefhttp://download.ros.org/schema/package_format3.xsd schematypenshttp://www.w3.org/2001/XMLSchema?
package format3nameclient/nameversion0.0.0/versiondescriptionclient test/descriptionmaintainer emailmikeqq.comzhi/maintainerlicenseTODO: License declaration/licensebuildtool_dependament_cmake/buildtool_dependdependrclcpp/dependdependtutorial_interfaces/dependtest_dependament_lint_auto/test_dependtest_dependament_lint_common/test_dependexportbuild_typeament_cmake/build_type/export
/package构建client包 回到工作空间根路径构建client.
$colcon build --packages-select client 在工作空间根路径build/client路径下(install/client/lib路径下同样生成)如果正常会有client_test可执行文件生成。 运行测试 我们可以直接运行前面创建的cpp_srvcli包里的server节点作为服务端或者构建上面写的service包。另开一个终端运行之前记得配置下环境source install/setup.bash。
$ros2 run cpp_srvcli server 再运行client_test。
$./build/client/client_test 23 3 5 上图右侧的终端窗口可以看到测试PASSED至此一个客户端的单元测试完成。 对于单元测试的知识点其实挺多的一时确实无法都掌握一篇内容也不能都概括当前我们也只需了解一个基本的用法即可后面随着实际碰到的问题越来越多再根据具体的问题钻研下去掌握到的知识点也就会水涨船高了。
本篇完。