徐彩霞 葛华勇 侯仰宇
(东华大学信息科学与技术学院 上海 201620)
命令行界面(Command-line Interface,CLI)是管理和配置网络设备的一种接口模式,几乎所有的网络设备都支持CLI接口模式[1]。由于CLI是通过基础函数开发得到的,其具有功能强大、易使用、扩充方便、资源占用率少等优点[2],所以客户端计算机、路由器和24小时运作的服务器等设备通常都采用CLI操作方式进行自动化的系统管理。因此,对客户端计算机、路由器和服务器等设备进行CLI测试必不可少。然而,目前的CLI测试方法大部分都使用手工测试,测试人员需要具备专业知识,熟悉相关操作的CLI指令。对于简单的回归测试,可以采用录制生成脚本的方式来提高一定的测试效率,但脚本的维护量大,复用性低。因此,整个CLI测试过程操作繁琐,手工输入易错,不易排错,而且要求对测试人员进行专业培训,增加了开发成本。特别是在回归测试中,极大地浪费了人力、时间和金钱。
为了改善CLI的测试现状,本文运用数据驱动脚本的方式[3],研究并实现了一种基于Python语言的CLI自动化测试方法。该方法包括四部分内容:使用python的unittest测试框架完成整体的测试工作;应用telnet协议和Python的telnetlib模块实现主机的远程登录;利用yaml的直观、与脚本语言的易交互性,将测试用例集合在yaml文件中;在用户界面输出测试结果,并将结果生成日志报告。使用此方法时,若一些测试用例改变,只需要改变数据文件,不需要改变测试脚本,提高了测试脚本的复用性。而且将测试数据整合到数据文件中,不需要每次测试都进行指令的手动输入,避免了手动输入指令的重复性、繁琐性,减少了手动出错的频率。同时,将结果生成日志报告,便于开发、测试人员的后期检查和调试。
本方法使用Python脚本语言,运用数据驱动脚本的测试思想,分离了测试脚本和测试用例,实现了没有宏命令的脚本语言自动化,是一种黑盒自动化测试方式[4],主要用于CLI模块的功能验证。该自动化测试方法如图1所示。
图1 自动化测试方法流程图
编译运行脚本文件时,整个测试过程是通过Python的单元测试框架unittest实现的。该单元测试框架首先初始化测试环境,然后执行测试工作流,最后,对测试结束后的测试环境进行清理和还原[5]。其中,执行测试工作流的具体顺序是:首先通过telnet协议远程登录主机设备;然后利用具体命令读取yaml数据文件,同时将其中的测试数据以参数的形式传递给脚本;脚本在客户端执行接收到的测试数据,即执行CLI指令;最后将执行结果输出到客户端界面和日志报告中。
基于Java的测试框架JUnit,Python衍生出了自身的单元测试框架unittest。利用unittest,测试人员能够以更加结构化的方式编写大型且周详的测试集。
unittest框架包括:测试用例(TestCase)、测试集合(TestSuite)、测试载入器(TestLoader)、测试运行器(TestRunner)、测试结果(TestResult)和测试夹具(TestFixture)[6]。它们之间的关系为:1)测试用例:包括测试前环境的初始化,执行测试的代码、步骤、预期结果等,以及测试后环境的清理还原;2)测试集合:多个测试用例集合在一起构成了测试集合;3)测试载入器:将测试用例加载到测试集合中的模块;4)测试运行器:用来执行测试集合的模块;5)测试结果:包括运行的测试用时,测试用例数目,执行成功的用例数目,执行失败的用例数目等信息;6)测试夹具:对一个测试用例环境的初始化和清理,通过覆盖测试用例的环境初始化和环境清理还原的方法来实现。
telnet协议是Internet远程登录服务的标准协议和主要方式[7]。用户应用telnet命令,使用户计算机暂时成为远程主机的仿真终端,之后在仿真终端的telnet程序中输入命令,这些命令会在主机上运行,与直接在主机的控制台上输入的效果一样。其工作原理如图2所示。
图2 telnet的工作原理图
telnet远程登录服务采用的是C/S(客户/服务器)模式,用户直接在客户端输入指令,仿真终端会把输入的指令通过telnet程序传递给服务器等网络设备,这些指令会在网络设备的外壳上运行,指令运行后的输出信息会通过telnet程序回显在仿真终端的显示界面上[8]。通常,客户端和主机设备之间的远程连接信息是通过TCP/IP协议进行传输的,在客户端输入的用户名、密码和命令字符等实际上就是向服务器端发送一个IP数据;远程操作结束时,断开telnet连接就是撤销TCP连接。通过telnet协议远程登录服务器需要满足的条件是:本地计算机支持telnet服务协议;知道远程主机的IP地址、登录标识用户名和密码。
yaml(Yet Another Markup Language另一种标记语言)是一种直观的、能够被电脑识别的数据序列化格式,是一种可读性高、容易和脚本语言交互,用来表达资料序列的编程语言。
本方法的测试数据是以yaml的格式进行保存并供测试脚本调用和执行。常见的数据存储方式有纯文本、数据库和 XML 文档、yaml文件等[9]。对于CLI的测试用例集合,纯文本形式可读性不高;数据库方式浪费资源;XML虽然易于数据交换和处理,但其语法比yaml复杂,解析成本高;yaml方式结构简单,实现方便,解析成本低,而且特别适合在脚本语语言中使用,因此本方法选择yaml文件格式存储CLI测试数据。
本文在Python的unittest测试框架上,运用tel⁃net协议和数据驱动脚本方式实现了CLI的自动化测试方法。本方法涉及到的unittest、telnetlib、yaml都是Python自带和支持的标准库函数[10],这些标准库的代码可用性高、功能强大、速度快,而且使用方便,只需要利用import语句直接导入,然后再根据各模块的使用方法分别进行配置调用即可。
unittest框架的基本使用方法是:首先是准备测试用例,然后利用测试载入器将测试用例加载到测试集合中,之后由测试运行器执行测试集合,执行结果保存在测试结果中。应用unittest模块实现CLI单元测试的部分程序代码如图3所示。
图3 unittest框架部分程序代码
本文首先在测试脚本中定义了一个测试用例类CLI_Test,该测试用例类继承自unittest.Test⁃Case[11]。在 CLI_Test类中,定义的方法包括:1)定义了setUp()方法,在此方法中写入了测试执行前的准备工作:初始化测试环境和创建日志文件;2)定义了tearDown()方法,在此方法中写入了测试结束后的环境还原和日志文件的关闭;3)定义了测试用例文件test_1_CLI,该文件主要包含了测试的启动、测试内容、测试方法、测试数据的执行、测试结果的判定及输出等。因此,通过对unittest模块进行正确的参数设置和方法定义,实现unittest的正常使用和CLI整体测试框架的搭建。
对服务器等网络设备进行CLI功能测试,首先要远程登录服务器等网络设备,本文利用python的telnetlib模块实现主机设备的远程登录,其代码如图4所示。
图4 telnet远程登录网络设备的代码
在已知远程主机的IP地址、标识和口令的情况下,通过telnetlib模块可以实现主机设备的远程登录。一般情况下,服务器的23端口主要用于tel⁃net服务[12],是 Internet上普遍采用的登录和仿真程序。因此,首先定义一个远程连接函数telnet_con⁃nect,它包括三个参数:主机host、用户名username和密码password;然后,通过设置tn=telnetlib.Tel⁃net(host,port=23,timeout=10)来连接主机设备的telnet服务,其中host可以是被测主机的IP地址或者域名;利用语句tn.write(username+“ ”)实现登录用户名的输入;经由语句tn.write(password+“ ”)实现登录密码的输入;凭借语句tn.expect([“system>”])来判断远程登录是否成功;最后,编写命令执行、退出Telnet服务的相关代码。
通过yaml数据文件整合所有的测试用例,文件结构可以通过缩进来表示,连续的项目可以通过减号“-”来表示[13]。部分数据文件格式如图5所示。
图5 yaml数据文件
整个yaml文件通过缩排方式呈现,整体结构简单,可读性高。当执行测试脚本时,将yaml文件中的数据以参数形式载入到测试脚本中,提高了脚本的复用性。文件中的Case是测试用例的ID号,便于测试结果的审查定位;Command是测试的CLI指令;Returncode是预期的测试结果,用于与实际运行结果作对比,从而判断测试是否成功。Case、Command、Returncode构成了一个简单的测试用例,所有的测试用例以同样的形式进行排列、整合后,就是一个完整的数据文件。
网络设备的CLI模块具备完整的功能集合,支持的功能有:SSH的连接与断开、命令的运行、运行结果的反馈等[14]。通常CLI指令运行结果的反馈情况是:执行成功会返回ok,执行失败就会返回相应的失败信息。因此,在yaml文件中设置Return⁃code为ok,并将其作为脚本中的expect_returncode,即 在 脚 本 中 定 义 :expect_returncode=str(yaml⁃file_load[Api][l][‘Returncode’]),将屏幕输出的实时信息作为脚本中的runtime_returncode,即在脚本中定义:runtime_returncode=output_format(screen_output,CLI_CMD),其中 CLI_CMD=Com⁃mand,screen_output=tn.read_very_eager(),Com⁃mand=str(yamlfile_load[Api][j][‘Command’])。若expect_returncode和runtime_returncode相等,则代表该条命令执行成功,否则代表执行失败。同时,将测试结果输出到终端界面。而且为了便于开发、测试人员调试CLI功能程序,将测试结果输出打印到日志中,日志文件包含测试用时、测试结果信息、测试用例总数、执行成功的测试用例数、执行失败的测试用例数等。
本方法主要根据returncode判断用例的测试结果,若runtime_returncode等于expect_returncode,则该条命令执行成功,否则执行失败。然后将测试结果输出到用户界面,并打印到日志中。用户界面的输出结果如图6所示。
图6 用户界面的输出结果
输出到用户界面的信息包括测试用例的基本信息:case ID、Command,Telnet情况,以及预期结果和运行结果。但是,若只将测试结果输出到用户界面是不方便的。因为CLI的测试用例很多,而且自动化测试执行的速度很快,用户界面不适合测试结果的统计、整理与审查。因此,同时将测试结果打印到日志中,便于测试结果的统计、整理与检查,有利于开发、测试人员的调试。该脚本共打印了三个日志文件,包含所有测试用例信息的详细日志和样例日志,仅包含测试失败用例信息的详细日志,满足开发、测试人员后期检查时的不同需求。所有测试用例的详细日志如图7所示。
图7 所有测试用例信息的详细日志
以包含8个测试用例的yaml文件为例,在所有测试用例的详细日志中,可以得到的信息有:测试开始时间、测试结束时间、测试用时、测试用例信息、测试结果信息、测试成功用例数、测试失败用例数等。当后期检查时,依照日志中的用例ID和Command可以快速定位和获取测试用例的具体信息;根据日志中的Runtime Returncode得到命令执行的实时输出信息和测试失败用例的错误原因,能够及时调试和改善功能程序的缺陷;凭借日志中的PASSED数目和FAILED数目快速统计测试用例的覆盖率和成功率,从而判断当前build的稳定性和有效性;通过日志中的used time可以看出每个测试用例的执行时间都控制在10s级别,比起每次手动输入节省时间。
本文应用数据驱动脚本方式,使用Python的单元测试框架研究并实现了一种面向CLI的自动化测试方法。该方法利用telnet协议实现主机设备的远程登录,并将测试用例从测试脚本中分离出来存放在外部的yaml数据文件中,实现了没有宏命令的脚本语言自动化,提高了脚本的复用性。而且将测试结果打印到日志文件中,便于开发、测试人员进行检查调试。与CLI的测试现状相比,该方法有效地避免了CLI测试工作的繁琐性、手动易错性,增强了测试脚本的复用度,极大地提高了测试效率、测试覆盖率,明显地减少了CLI功能的开发成本。而且选用yaml,提高了数据文件的可读性,应用人群更广;利用Python的强大功能库进行脚本的编写,代码简洁,开发简单。
该测试方法主要应用于CLI的功能测试环节,下一步可以将源代码的分析模块、测试用例的生成模块也集成到该方法中,并考虑提供一个用户交互页面便于测试人员的操作。
[1]姚林燕.CLI中命令树的设计和实现[J].信息通信,2012(1):184-186.
YAO Linyan.Design and Implementation of Command Tree in CLI[J].Information&Communications,2012(1):184-186.
[2]李晨.基于CLI的IPRAN网络管理系统框架的研究和实现[D].武汉:武汉邮电科学研究院,2014:9-18.
LI Chen.Research and Realization of CLI-Based IP RAN NMS Framework[D].Wuhan:Wuhan Research Institute of Posts and Telecommunications,2014:9-18.
[3]陈琪.自动化测试平台的设计与实现[D].西安:西安电子科技大学,2014:5-18.
CHEN Qi.Design and Implementation of Test Automation Platform[D].Xian:Xidian University,2014:5-18.
[4]李鑫.基于Python的软件测试自动化平台[D].太原:太原科技大学,2014:17-41.
LI Xin.Software Testing Automation Platform Based on Py⁃thon[D].Taiyuan:Taiyuan University of Science&Tech⁃nology,2014:17-41.
[5]蒋子豪.基于Appium的移动端自动化测试项目的设计与实现[D].南京:南京大学,2016:43-54.
JIANG Zihao.The Design and Implementation of Appi⁃um-based Automated Testing Project on Mobile Platforms[D].Nanjing:Nanjing University,2016:43-54.
[6]江屿.基于Junit的接口测试框架的设计与实现[D].南京:东南大学,2015:6-13.
JIANG Yu.Design and Realization of the Test Framework of Interface Based on Junit[D].Nanjing:Southeast Univer⁃sity,2015:6-13.
[7]周开波,张治兵,倪平,等.网络设备Telnet服务安全威胁及其防范措施[J].现代电信科技,2016,46(3):11-15.
ZHOU Kaibo,ZHANG Zhibing,NI Ping,et al.Security threats and counter measures related to Telnet of Network devices[J].Modern Science&Technology of Telecommu⁃nications,2016,46(3):11-15.
[8]胡奕,唐莉萍.嵌入式系统上无操作系统Telnet服务器的实现[J].计算机系统应用,2014,23(10):79-84.
HU Yi,TANG Liping.Implementation of Telnet Server for Non-OS Embedded System[J].Computer System Applica⁃tion,2014,23(10):79-84.
[9]崔红军,饶若楠,邵培南.一种API自动化测试工具的设计与实现[J].计算机工程,2007,33(4):270-274.
CUI Hongjun,RAO Ruonan,SHAO Peinan.Design and Im⁃plementation of An Automated API Test Tool[J].Comput⁃er Engineering,2017,33(4):270-274.
[10]Magnus Lie Hetland.Python基础教程[M].司维,曾军崴,谭颖华译.第2版.北京:人民邮电出版社,2010:277-290.
Magnus Lie Hetland.Basic Course of Python[M].Si Wei,ZENG Junwei,TAN Yinghua translation.The Second Edition.Beijing:Posts and Telecommunications Press,2010:277-290.
[11]白凯,崔冬华.基于JUnit自动化单元测试的研究[J].计算机与数字工程,2010,38(2):52-54.
BAI Kai,CUI Donghua.Research of Automatic Unit Test Based on Junit[J].Computer&Digital Engineering,2010,38(2):52-54.
[12]张国防.基于telnet协议的Linux远程管理[J].网络安全技术与应用,2014(10):53-54.
ZHANG Guofang.TheremotemanagementofLinux based telent[J].Network Security Technology and Appli⁃cation,2014(10):53-54.
[13]M Eriksson,V Hallberg.Comparison between JSON and YAML for data serialization[EB/OL].(2011)[2016].http://www.csc.kth.se/utbildning/kandidatexjobb/datateknik/2011/rapport/eriksson_malin_OCH_hall⁃berg_victor_K11047.pdf.
[14]刘益,朱晓民.资源池中CLI模块化网络配置管理设计方案[J].电信工程技术与标准化,2016,29(1):44-48.
LIU Yi,ZHU Xiaomin.Design scheme of network config⁃uration management of CLI template in resource pool[J].Telecom EngineeringTechnicsand Standardization,2016,29(1):44-48.