终于到了这一系列的最后一期,每一次真的比较纠结,因为工作实在太忙了,每一次都纠结是不是要放弃,每一次又坚持了下来,为自己打个Call,其实这个系列真的非常有意义,记录了我从只会作黑盒测试的正宗黑盒测试人员,慢慢的有了具备开发协助黑盒测试的自动化工具,这个工具从被别人鄙视“搞什么搞”的低级阶段渐渐发展成有班长,成规模,让别人觉得“有点样子”的中级阶段,再到这次真的有了界面,一个真的有模有样,让别人可以另眼相看的阶段,这个期间的痛并快乐着的感受,真的只有经历的人才会知道。但是大家千万不要以为这个完结篇就代表了女巫目前的工作level,这个“完结篇”是2016年的“完结篇”,因为从2016年做了这个完结篇,真的主管才真的重视我们的自动化工具,然后从2017年开始,真的黑盒测试任务渐渐变少,到2019年真的几乎已经没有了黑盒测试任务,自动化工作反而成了工作的重点,所以真觉得还挺神奇的,几年前的非本职工作,被别人在质疑搞什么搞得自动化,现在变成了主业,真的很多时候你不去努力一把,你不知道自己有多厉害哈哈。
好吧,心路历程总结完毕,我们真的可以开始介绍我们的工具,我们的工具
比较厉害,有了界面,这个界面是用C++实现,我们是在C++中调用Python脚本的方式来实现我们的自动化
一、如何配置C++与Python结合编程的编程环境
1.Python的资料Copy
首先将Python安装目录下的Libs和include文件copy下来,如下图
2.VS端的设置
VS需要创建一个MFC的项目,这里就不再赘述,之前的C++已经叙述过如何创建,创建完毕请注意
1)运行的模式如果选择debug Mode如下图
a.需要将Python的Include和Libs copy到我们建立的C++ project的路径下,如下图
b.Include文档的配置,选择项目->属性->C/C++的”一般”->其它include 目录选择a 中copy的include所在的路径
c.Libs 配置依赖项路径
选择项目->属性->连接器->一般->其它程式库目录,如下图:
d.在Debug中运行,需要设置依赖项
首先需要在libs文件夹下的Python27.lib复制一个,并将其名称改为python27_d.lib,如下图
然后到选择项目->属性->连接器->输入路径下,将python27_d.lib设置到“其它相依性”此栏位中,如下图
e.配置“先行编译标头档”
需要选择“未使用先行编译表头档”如下图:
2)运行模式如果选择Release Mode如下图
除了不需要设置依赖项即步骤d,其它与选择Debug一样
3)例子说明:
下面的例子即为可以编译成功的例子
二、Python C++ API 的学习
1.资料来源:地址
Python的官网:Https://docs.python.org/2/c-api/index.html
注意选择的是Python 2.7.11这个与我们安装的Python的版本一样:我们安装的就是Python27。如【图1】
【图1】
2.具体的Topic
Python/C API Reference Manual
此Topic中一共有10个分类,建议不要每个分类都看,因为其中的信息量太大了,可以以实现的功能为中心,选择性的学习这些接口函数。
10个分类分别是:Introduction;The very High Level Layer;Reference
Counting;Exception Handing;Utilities;Abstract;Concrete Objects
Layer;Initialization, Finalization,
3.常用接口函数
1)Initialization, Finalization and Threads
Void Py_Initialize (void)
初始化Python解释器,在一个内嵌Python的应用软件中,在调用其它的API函数前,必须要调用此函数。此函数没有参数,没有返回值,且如果初始化失败就会出现致命的失败。
Int Py_IsInitalized()
判断是否初始化成功,如果成功返回值是True(非0)否则为False(0);此函数没有参数。
Py_Finalize()
取消所有的Py_Initialize()此函数做的初始化,且摧毁所有已经做过的初始化。此函数没有参数,没有返回值
2)The very High Level Layer
此分类主要是执行一个文档或者缓冲区中的源代码
PyRun_SimpleString(const char *command)
它是PyRun_SimpleStringFlags(const char *command PyCompiler *flags)的简化版,即只需要提供一个参数即可,这句命令的意思是执行Python中的一段代码。
PyObject *PyEval_CallObject(PyObject*pfunc,PyObject*pargs)
第一个参数是Module中需要调用的函数,是通过此函数获得PyObject_GetAttrString(此函数在下面会讲到) pargs是函数的参数列表,通常Py_BuildValue(char*format,……)来构建。
Int PyRun_AnyFile(FILE *fp, const char *filename)
该API的作用是运行名称为filename的python脚本
3)Concrete Objects Layer
这个分类主要是明确特定的某些Python的对象类型,传送一个对象必须需要搞清楚它是什么类型,同样如果你接收一个对象必须知道这个对象是什么类型的,如果不知道首先需要check它是什么类型的,例如如果希望确认这个对象是否为字典类型,则需要使用PyDict_Check()这个函数。这个章节就是讲述Python对象类型的结构图
Python对象类型主要分为基本的对象,数字类型对象,序列类型对象,映射类型对象,其它对象
序列类型对象中的String/Bytes 对象类型
这些对象包含的函数,如果获得的参数不是string类型的参数,则会返回一个TypeError的错误。
PyObject* PyString_FromString(const char *v)
将参数中的字串常量转成一个字串对象。
Module Objects
PyObject * PyModule_GetDict(PyObject *module)
返回一个字典类型的对象,此对象包含模块名称。此对象与模块对象的__dict__属性是相同的。这个函数不会fail,如没有会返回
File Objects
PyObject* PyFile_FromString(char *filename,char *mode) |
返回一个打开的文件类型的Python对象,该对象的打开方式由mode指定,mode的取值同C语言的函数fopen():
FILE* PyFile_AsFile(PyObject *p) |
将与p相关联的文件对象作为FILE指针
4)Import Modules
PyObject *PyImport_ImportModule(const char *name)
导入一个Python模块,参数Name可以是“.py”文件的文件名,类似Python内建函数Import
注意:参数是const char类型,即调用时直接写PyImport_ImportModule("candpython")即可,candpython就是需要调用的模块的名称即“.py”文件的文件名称。
PyObject*PyImport_Import(PyObject *name)
作用与上面函数一样,但是注意其需要传进来的参数是PyObject类型,不是简单的char类型,所以需要将模块名称使用
PyString_FromString(const char *v)转换成PyObject类型才可以使用这个函数,这两个函数Mary均做过实现,可以实现相同功能
5)Object Protocol
PyObject *PyObject_GetAttrString(PyObject*o, const char *attr_name )
两个参数,第一个参数是导入“. py ”的module 对象,第二个参数是此module对象中的字串,这个函数的意义是获得此Module中的函数名称字串,并将此字串转成Object类型。
Int PyObject_HasAttr(PyObject *o,PyObject *attr_name)
两个参数,第一个参数是导入“. py ”的module 对象,第二个参数是此module对象中的字串,这个函数的意义是确认Module中是否有attr_name此字串
注意:这个函数传进去的第二个参数为Object所以如果使用这个函数,需要先将字串类型转成object类型。
如果能找到,返回值为1否则为0,注意无论是否能找到,这个函数总是执行成功的,不会有错误的现象产生。
6)Int PyObject_HasAttrstring(PyObject*o, const char *attr_name )
与2)都一样,唯一不同的是传入的第二个参数是Const char类型,不需要将其转成Object。
三、代码例子
1、不需要传入参数的简单Python内嵌例子
说明:
1)初始化Py_Initialize();
2)直接运行Python的代码,这段代码的意思就是添加当前的路径
PyRun_SimpleString(“import sys”) PyRun_SimpleString("sys.path.append('./')"); |
3)载入一个名字叫candpython的脚本
pName=PyString_FromString("candpython"); pModule =PyImport_ImportModule("candpython"); |
4)运行candpython的脚本中的函数
pFunc=PyObject_GetAttrString(pModule,"getlog"); PyEval_CallObject(pFunc,NULL); |
5)结束初始化 Py_Finalize();
2.需要传入参数的Python内嵌例子之较为复杂的实现
与不需要传入参数一样的内容不再赘述。
1)首先创建一个元组类型,元组的大小是2
2)将参数从C可以理解的内容转成Python可以读懂的内容
3)将此两个参数插入到一个元组对象中
4)调用此函数以及函数的参数即可
3.需要传入参数的Python内嵌例子之较为简单的实现
1)将C能理解数据翻译成Python能读懂的对象类型
注意这里是将两个参数直接翻译,并没有一个一个的翻译
2)调用函数以及此函数的两个参数
4.Python返回值传回给C++
该Python脚本执行的是一个加法运算,先从控件获得加数和被加数,将其转换为Python对象并传给Python脚本,执行脚本后得到结果,再将此结果转换为C++数据类型回传给C++,并在控件中显示。
该脚本Python脚本执行的是at command的压力测试,先从控件获得COM口,将其转换为Python对象传给Python脚本,执行完脚本后,得到测试总次数,Pass次数和Fail次数,再将此结果转换为C++数据类型回传给C++,并在控件中显示。由于需要转换的Python对象有多个,所以此处用的是int PyArg_ParseTuple(PyObject *args, const char *format,…)
5.运行Python脚本
运行脚本使用PyRun_AnyFile(FILE *fp, const char *filename),为了得到FILE指针,需要先将Python脚本以只读方式打开并转换为Python对象,再将其转换为FILE指针,
四、实际项目说明
1.简单例子
Atcommand2.exe就是C++的执行档案,承担的是界面的工作
Atgpioreq2.py是界面中调用的脚本
其实点击“确认”按键就是让下面的脚本执行,这样看起来是不是漂亮很多!
2.较漂亮的例子
如果你觉得上面的界面太简单了,看一下以下的例子:
一个非常漂亮的两个界面,其中汇集了边界测试,压力测试,而且考虑到每个项目需要将一些参数开放出来给使用者使用,对于使用者非常方便,需要测试什么项目,选择什么项目,根据项目的情况设置具体的参数,然后直接点击run就可以自动执行python的脚本,真的很酷!
这一系列告一段落,这个系列对于女巫的职业生涯真的意义非凡,对于别人的冷嘲热讽,只要内心够坚持,每天都在进步,日积月累真的就会产生奇妙的结果,当我们把这个有界面的测试工具拿出来时,老板就开始慢慢减少我们team的黑盒测试工作,直到现在几乎没有黑盒测试工作,还要感谢那些质疑我们搞什么搞得同事,虽然他们依然还在做着一成不变的工作……所以还是那句话:路漫漫其修远兮,吾将上下而求索……加油!
......
查看更多精彩内容,请点击下载:
版权声明:本文出自《51测试天地》第五十三期。51Testing软件测试网及相关内容提供者拥有51testing.com内容的全部版权,未经明确的书面许可,任何人或单位不得对本网站内容复制、转载或进行镜像,否则将追究法律责任。