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的更多相关文章

  1. ROS系统C++代码测试之gtest

    1. 安装gtestsudo apt-get install libgtest-dev 2.修改CMakeLists.txtfind_package(GTest REQUIRED)uncommend ...

  2. ros 使用python代码启动launch文件

    在开发中我们经常会遇到使用python代码启动launch文件这样的问题.一般的做法是使用subprocess调用roslaunch.但是这种方法使用起来并不方便.要涉及到自己去控制进程的状态.由于r ...

  3. 光伏电池测控系统python代码

    '''硬件keithley万用表和程控电源visa是VXIplug&play系统联盟制定的一套标准.python实现VISA,形成pyviva模块'''###IV测试系统的部分程序代码 fro ...

  4. 树莓派(Raspbian系统)中使用pyinstaller封装Python代码为可执行程序

    一.前言 将做好的Python软件运行在树莓派上时,不想公开源码,就需要对文件进行封装(或称打包),本文主要介绍使用pyinstaller封装Python代码为可执行程序. Python是一个脚本语言 ...

  5. 基于深度学习的人脸性别识别系统(含UI界面,Python代码)

    摘要:人脸性别识别是人脸识别领域的一个热门方向,本文详细介绍基于深度学习的人脸性别识别系统,在介绍算法原理的同时,给出Python的实现代码以及PyQt的UI界面.在界面中可以选择人脸图片.视频进行检 ...

  6. (二)ROS系统架构及概念 ROS Architecture and Concepts 以Kinetic为主更新 附课件PPT

    第2章 ROS系统架构及概念 ROS Architecture and Concepts PPT说明: 正文用白色,命令或代码用黄色,右下角为对应中文译著页码. 这一章需要掌握ROS文件系统,运行图级 ...

  7. SLAM+语音机器人DIY系列:(二)ROS入门——2.ROS系统整体架构

    摘要 ROS机器人操作系统在机器人应用领域很流行,依托代码开源和模块间协作等特性,给机器人开发者带来了很大的方便.我们的机器人“miiboo”中的大部分程序也采用ROS进行开发,所以本文就重点对ROS ...

  8. 【转载】ROS系统整体架构

    目录 1.从文件系统级理解 2.从计算图级理解 3.从开源社区级理解 由于ROS系统的组织架构比较复杂,简单从一个方面来说明很难说清楚.按照ROS官方的说法,我们可以从3个方面来理解ROS系统整体架构 ...

  9. ROS-2 : ROS系统层级结构

    一.ROS文件系统层级 ROS的文件和文件夹按如下层级来组织:

随机推荐

  1. ORACLE与mysql中查询第n条到第m条的数据记录的方法

    ORACLE: SELECT * FROM             (                  SELECT 表名.*, ROWNUM AS CON FROM 表名 WHERE ROWNUM ...

  2. mysql 5.5多实例部署【图解】

    mysql5.5数据库多实例部署,我们可以分以下几个步骤来完成. 1. mysql多实例的原理 2. mysql多实例的特点 3. mysql多实例应用场景 4. mysql5.5多实例部署方法 一. ...

  3. 【转】ETL增量抽取——通过时间戳方式实现

    这个实验主要思想是在创建数据库表的时候, 通过增加一个额外的字段,也就是时间戳字段, 例如在同步表 tt1 和表 tt2 的时候, 通过检查那个表是最新更新的,那个表就作为新表,而另外的表最为旧表被新 ...

  4. SQL Server调优系列进阶篇(深入剖析统计信息)

    前言 经过前几篇的分析,其实大体已经初窥到SQL Server统计信息的重要性了,所以本篇就要祭出这个神器了. 该篇内容会很长,坐好板凳,瓜子零食之类... 不废话,进正题 技术准备 数据库版本为SQ ...

  5. c# 注册全局热键

    //引入系统API [DllImport("user32.dll")] static extern bool RegisterHotKey(IntPtr hWnd, int id, ...

  6. gulp系列:简单实践

    coffescript测试源码   gulp = require('gulp') #删除 1.清空目录 常用插件 gulp-clean .del (nodejs模块) del = require('d ...

  7. spring管理bean

  8. linux清理内存命令

    1.清理前内存使用情况 free -m 2.开始清理  echo 1 > /proc/sys/vm/drop_caches3.清理后内存使用情况 free -m4.完成! 查看内存条数命令: # ...

  9. linux开机自动连接无线网络

           1.右击无线网络图标的“编辑连接”. 2.在“无线”选项卡里,选择“编辑”. 3.在“无线安全性”选项卡里,输入无线密匙,并选中左下角的“对所有用户可      用”的选项点击应用,会提 ...

  10. 中午游泳很海皮-linux&php

    hi 中午又去游泳了,其实本来打算是昨天去的,谁知天公不作美,周一都下雨.今天其实也一样的,有点小雨,不过游得到泳,比什么都好 1.PHP&MySQL -----PHP内置MySQL函数学习( ...