ROS系统python代码测试之rostest
ROS系统中提供了测试框架,可以实现python/c++代码的单元测试,python和C++通过不同的方式实现,
之后的两篇文档分别详细介绍各自的实现步骤,以及测试结果和覆盖率的获取。
ROS系统中python代码测试介绍
关于测试代码的写法细节请参考官方wiki文档,http://wiki.ros.org/unittest,本文主要说明使用中的坑。
ROS中python代码的测试可以有两种实现方式一是节点级的集成测试,可以测试节点创建和消息收发的整个过程;二是代码级的单元测试,在测试用例中导入被测代码进行测试。
python代码测试中可能遇到的问题及优化修改
1、创建启动测试的roslaunch文件
rostest相关的roslaunch请参考 http://wiki.ros.org/roslaunch/XML/test
<launch>
<node name="nodename" pkg="pkgname" type="被测文件的py"/>
<test test-name="testnodename" pkg="pkgname" time-limit="500.0" type="测试文件py" args="--cov"/>
</launch>
关注点1:
time-limit这个标签限制了用例运行的最长时间,如果用例耗时超过这个时间,那么用例会自动以超时失败结束,默认值是60s。如果用例较多运行时间长的话,需要
设置合理的值;
关注点2:
args 这个标签可以向测试程序中传入参数,--cov的作用是测试完成后生成覆盖率文件,下面详细介绍。
2、测试结果文件获取
参见上一篇介绍环境变量了文章,通过ROS_TEST_RESULTS_DIR这个变量可以修改测试结果输出的位置。
3、覆盖率统计
我使用的ros版本是indigo,这个版本中rostest对覆盖率统计的代码需要进一步优化。
优化点1:rostest.rosrun(package_name, test_name, test_case_class, sysargs=None)
根据wiki中对rostest.rosrun的描述,该函数应该有第五个参数coverage_packages,该参数表示待测试package list.
优化后的函数rostest.rosrun(package_name, test_name, test_case_class, sysargs=None, coverage_packages=None)
优化点2:rostest覆盖率统计完善
覆盖率统计需要首先a安装python代码覆盖率工具coverge,参考http://coverage.readthedocs.org/en/latest/
修改rostest.rosrun代码,使代码能够输出xml_report,为什么要输出xml报告呢,因为The report is compatible with Cobertura reports.
这一点很关键,在jenkins持续集成环境中需要这一步骤的工作。jenkins中的Cobertura插件可以解析xml_report文件,然后将python代码的详细覆盖率信息显示在用例的测试结果中。
/opt/ros/indigo/lib/python2.7/dist-packages/rostest/__init__.py 中函数的修改
def rosrun(package, test_name, test, sysargs=None, coverage_packages=None):
"""
Run a rostest/unittest-based integration test. @param package: name of package that test is in
@type package: str
@param test_name: name of test that is being run
@type test_name: str
@param test: test class
@type test: unittest.TestCase
@param sysargs: command-line args. If not specified, this defaults to sys.argv. rostest
will look for the --text and --gtest_output parameters
@type sysargs: list
"""
if sysargs is None:
# lazy-init sys args
import sys
sysargs = sys.argv #parse sysargs
result_file = None
for arg in sysargs:
if arg.startswith(XML_OUTPUT_FLAG):
result_file = arg[len(XML_OUTPUT_FLAG):]
text_mode = '--text' in sysargs
coverage_mode = '--cov' in sysargs
if coverage_mode:
_start_coverage(coverage_packages) import unittest
import rospy coverresult = os.getenv('ROS_TEST_RESULTS_DIR') + '/coverage/' suite = unittest.TestLoader().loadTestsFromTestCase(test)
if text_mode:
result = unittest.TextTestRunner(verbosity=2).run(suite)
else:
result = rosunit.create_xml_runner(package, test_name, result_file).run(suite)
if coverage_mode:
_stop_coverage(coverage_packages, coverresult)
rosunit.print_unittest_summary(result) # shutdown any node resources in case test forgets to
rospy.signal_shutdown('test complete')
if not result.wasSuccessful():
import sys
sys.exit(1)
def _stop_coverage(packages, html=None):
"""
@param packages: list of packages to generate coverage reports for
@type packages: [str]
@param html: (optional) if not None, directory to generate html report to
@type html: str
"""
if _cov is None:
return
import sys, os
try:
_cov.stop()
# accumulate results
_cov.save() # - update our own .coverage-modules file list for
# coverage-html tool. The reason we read and rewrite instead
# of append is that this does a uniqueness check to keep the
# file from growing unbounded
if os.path.exists('.coverage-modules'):
with open('.coverage-modules','r') as f:
all_packages = set([x for x in f.read().split('\n') if x.strip()] + packages)
else:
all_packages = set(packages)
with open('.coverage-modules','w') as f:
f.write('\n'.join(all_packages)+'\n') try:
# list of all modules for html report
all_mods = [] # iterate over packages to generate per-package console reports
for package in packages:
pkg = __import__(package)
m = [v for v in sys.modules.values() if v and v.__name__.startswith(package)]
all_mods.extend(m) # generate overall report and per module analysis
_cov.report(m, show_missing=0)
for mod in m:
res = _cov.analysis(mod)
print("\n%s:\nMissing lines: %s"%(res[0], res[3])) if html: print("="*80+"\ngenerating html coverage report to %s\n"%html+"="*80)
_cov.html_report(all_mods, directory=html)
_cov.xml_report(all_mods, outfile=html + 'cover.xml')
except ImportError as e:
print("WARNING: cannot import '%s', will not generate coverage report"%package, file=sys.stderr)
except ImportError as e:
print("""WARNING: cannot import python-coverage, coverage tests will not run.
To install coverage, run 'easy_install coverage'""", file=sys.stderr)
/opt/ros/indigo/lib/python2.7/dist-packages/rosunit/pyunit.py 文件修改
def unitrun(package, test_name, test, sysargs=None, coverage_packages=None):
"""
Wrapper routine from running python unitttests with
JUnit-compatible XML output. This is meant for unittests that do
not not need a running ROS graph (i.e. offline tests only). This enables JUnit-compatible test reporting so that
test results can be reported to higher-level tools. WARNING: unitrun() will trigger a sys.exit() on test failure in
order to properly exit with an error code. This routine is meant
to be used as a main() routine, not as a library. @param package: name of ROS package that is running the test
@type package: str
@param coverage_packages: list of Python package to compute coverage results for. Defaults to package
@type coverage_packages: [str]
@param sysargs: (optional) alternate sys.argv
@type sysargs: [str]
"""
if sysargs is None:
# lazy-init sys args
import sys
sysargs = sys.argv import unittest if coverage_packages is None:
coverage_packages = [package] #parse sysargs
result_file = None
for arg in sysargs:
if arg.startswith(XML_OUTPUT_FLAG):
result_file = arg[len(XML_OUTPUT_FLAG):]
text_mode = '--text' in sysargs coverage_mode = '--cov' in sysargs or '--covhtml' in sysargs
if coverage_mode:
start_coverage(coverage_packages) # create and run unittest suite with our xmllrunner wrapper
suite = unittest.TestLoader().loadTestsFromTestCase(test)
if text_mode:
result = unittest.TextTestRunner(verbosity=2).run(suite)
else:
result = create_xml_runner(package, test_name, result_file).run(suite)
if coverage_mode:
#cov_html_dir = 'covhtml_test' if '--covhtml' in sysargs else None
cov_html_dir = os.getenv('ROS_TEST_RESULTS_DIR') + '/coverage/'
stop_coverage(coverage_packages, html=cov_html_dir) # test over, summarize results and exit appropriately
print_unittest_summary(result) if not result.wasSuccessful():
import sys
sys.exit(1)
def stop_coverage(packages, html=None):
"""
@param packages: list of packages to generate coverage reports for
@type packages: [str]
@param html: (optional) if not None, directory to generate html report to
@type html: str
"""
if _cov is None:
return
import sys, os
try:
_cov.stop()
# accumulate results
_cov.save() # - update our own .coverage-modules file list for
# coverage-html tool. The reason we read and rewrite instead
# of append is that this does a uniqueness check to keep the
# file from growing unbounded
if os.path.exists('.coverage-modules'):
with open('.coverage-modules','r') as f:
all_packages = set([x for x in f.read().split('\n') if x.strip()] + packages)
else:
all_packages = set(packages)
with open('.coverage-modules','w') as f:
f.write('\n'.join(all_packages)+'\n') try:
# list of all modules for html report
all_mods = [] # iterate over packages to generate per-package console reports
for package in packages:
pkg = __import__(package)
m = [v for v in sys.modules.values() if v and v.__name__.startswith(package)]
all_mods.extend(m) # generate overall report and per module analysis
_cov.report(m, show_missing=0)
for mod in m:
res = _cov.analysis(mod)
print("\n%s:\nMissing lines: %s"%(res[0], res[3])) if html: print("="*80+"\ngenerating html coverage report to %s\n"%html+"="*80)
_cov.html_report(all_mods, directory=html)
_cov.xml_report(all_mods, outfile=html + 'cover.xml')
except ImportError as e:
print("WARNING: cannot import '%s', will not generate coverage report"%package, file=sys.stderr)
except ImportError as e:
print("""WARNING: cannot import python-coverage, coverage tests will not run.
To install coverage, run 'easy_install coverage'""", file=sys.stderr)
4、测试代码例子
if __name__ == '__main__':
for arg in sys.argv:
print arg
testfiles = []
testfiles.append('被测试的python package')
testfiles.append('被测试的python package')
rostest.rosrun(PKG, NAME, 测试类名, sys.argv, testfiles)
ROS系统python代码测试之rostest的更多相关文章
- ROS系统C++代码测试之gtest
1. 安装gtestsudo apt-get install libgtest-dev 2.修改CMakeLists.txtfind_package(GTest REQUIRED)uncommend ...
- ros 使用python代码启动launch文件
在开发中我们经常会遇到使用python代码启动launch文件这样的问题.一般的做法是使用subprocess调用roslaunch.但是这种方法使用起来并不方便.要涉及到自己去控制进程的状态.由于r ...
- 光伏电池测控系统python代码
'''硬件keithley万用表和程控电源visa是VXIplug&play系统联盟制定的一套标准.python实现VISA,形成pyviva模块'''###IV测试系统的部分程序代码 fro ...
- 树莓派(Raspbian系统)中使用pyinstaller封装Python代码为可执行程序
一.前言 将做好的Python软件运行在树莓派上时,不想公开源码,就需要对文件进行封装(或称打包),本文主要介绍使用pyinstaller封装Python代码为可执行程序. Python是一个脚本语言 ...
- 基于深度学习的人脸性别识别系统(含UI界面,Python代码)
摘要:人脸性别识别是人脸识别领域的一个热门方向,本文详细介绍基于深度学习的人脸性别识别系统,在介绍算法原理的同时,给出Python的实现代码以及PyQt的UI界面.在界面中可以选择人脸图片.视频进行检 ...
- (二)ROS系统架构及概念 ROS Architecture and Concepts 以Kinetic为主更新 附课件PPT
第2章 ROS系统架构及概念 ROS Architecture and Concepts PPT说明: 正文用白色,命令或代码用黄色,右下角为对应中文译著页码. 这一章需要掌握ROS文件系统,运行图级 ...
- SLAM+语音机器人DIY系列:(二)ROS入门——2.ROS系统整体架构
摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS ...
- 【转载】ROS系统整体架构
目录 1.从文件系统级理解 2.从计算图级理解 3.从开源社区级理解 由于ROS系统的组织架构比较复杂,简单从一个方面来说明很难说清楚.按照ROS官方的说法,我们可以从3个方面来理解ROS系统整体架构 ...
- ROS-2 : ROS系统层级结构
一.ROS文件系统层级 ROS的文件和文件夹按如下层级来组织:
随机推荐
- servlet 学习(一)
一.Servlet简介 Servlet是sun公司提供的一门用于开发动态web资源的技术. Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向 ...
- jstree动态生成树
前篇文章简单介绍了静态生成树,这篇文章将通过后台把数据通过json形式传到前台,进行动态生成树. 本篇的程序所用框架为Spring MVC,可以很方便的通过controller层传json到前台. 前 ...
- .net开发windows服务小结
今天学习了在.net下创建一个windows服务,总结一下学习心得. 开发环境:visual studio 2012 一.编写程序 (1)创建一个空解决方法 (2)添加一个控制台应 ...
- Access restriction: The type 'RSACipher' is not API
解决方法: http://stackoverflow.com/questions/860187/access-restriction-on-class-due-to-restriction-on-re ...
- Spark SQL 之 Migration Guide
Spark SQL 之 Migration Guide 支持的Hive功能 转载请注明出处:http://www.cnblogs.com/BYRans/ Migration Guide 与Hive的兼 ...
- 烂泥:学习ssh之ssh密钥随身携带
本文由秀依林枫提供友情赞助,首发于烂泥行天下 在上一篇文章<烂泥:学习ssh之ssh无密码登陆>中,我们讲解了如何使用ssh密钥,免密码登陆服务器. 这篇文章我们再来讲解,如何把已经生成的 ...
- 在Windows下配置Python+Django+Eclipse开发环境
一.配置开发环境我的开发环境是:Python2.6.7 + Django1.6.2 + Eclipse1.安装Python2.安装Eclipse的Python插件PyDev如上两步如何操作请点击此进行 ...
- LNMP环境搭建
LNMP环境搭建 Linux + Nginx + MySQL + PHP PHP是一种脚本语言,当前中国乃至世界上使用PHP语言开发的网站非常普遍 Nginx是一个web服务软件,和apache是一类 ...
- [转]关于信息安全认证CISP与CISSP的对比及分析
本文转自:https://www.douban.com/group/topic/89081816/ 最近好多信息安全行业或者打算转行的职场人在纠结学CISP还是学CISSP,我给大家就CISP和CIS ...
- [转]Ionic系列——CodePen上的优秀Ionic_Demo
本文转自:http://my.oschina.net/u/1416844/blog/514361?fromerr=bbFC5JIl 案例网站 Slidebox with Dynamic Slides ...