在写app自动化用例时,尝试用了关键字驱动的框架
记录一下自己对关键字驱动的理解:
1 关键字驱动指将用例步骤的操作封装为关键字,比如定位元素、点击元素、获取元素属性值、断言,这些都是操作关键字
2 在excel中按照用例执行过程,填写操作关键字以及要执行操作指令需要用到的参数信息及操作后保存的对象名
3 封装的代码框架,可以实现读取excel用例文件,拿到关键字操作及对应的参数
4 然后按照关键字动作传入对应的参数,去执行
关键点:
1)将用例步骤抽象出关键字
2)怎么执行excel中存放的字符串对应的方法
3)怎么将执行的结果存放到excel指定名称的变量
关键点的解决思路:
ui用例主要的动作就是找元素、点击、输入、执行脚本、断言、获取属性,其他特殊的需要单独取思考封装
在模型中按照具体的action类型去判断:
if action_ == 'find':
#执行find_element
if action_ =='send_keys':
#对指定元素做输入操作
这里使用的是string的反射机制、利用类对象的__getattribute__(funcname)()实现动态调用类对象的方法
比如:find操作分为find_element、find_elements,那怎么让driver执行具体的函数?
driver.__getattribute__(find_)(getattr(AppiumBy,selector_),selector_value_)
其中find_是获取的excel定义的调用哪个find方法,值是find_element、find_elements
driver.__getattribute__(find_)就是返回find_element或find_elements函数的引用,就是driver.find_element()或driver.find_elements
getattr(AppiumBy,selector_)是按照excel中指定的定位类型XPATH、ID动态获取AppiumBy类变量值

driver.__getattribute__(find_)(getattr(AppiumBy,selector_),selector_value_)就等于
driver.find_elements(AppiumBy.ID,selector_value_) 假设selector_是ID
先解释一下场景:
通过driver.__getattribute__(find_)(getattr(AppiumBy,selector_),selector_value_)返回了一个Weblement对象,需要赋值变量名为excel中定义的save_object值。假设save_object的值为ele1
如果直接写save_object =driver.__getattribute__(find_)(getattr(AppiumBy,selector_),selector_value_)
那save_object 这个变量的值的appUI 元素,而不是ele1=ui元素赋值
如何给指定变量名的变量赋值?
可以通过自定义一块空间,然后对内存空间进行属性名、属性值的赋值 setattr()
实例化类的时候就会申请一块空间
故创建一个Context类,在需要赋值给设定的变量时
使用setattr(context,destname,destvalue)去设定,还是看刚才的例子,可以通过下面代码将find_element()返回的元素赋值给save_obj_指定的变量名
setattr(Context,save_obj_,driver.__getattribute__(find_)(getattr(AppiumBy,selector_),selector_value_))
后续通过getattr(context,obj_name)拿到obj_name这个变量名的值
下面是具体的框架实现,仅简单实现了3个用例,(关键字驱动的util中没有做封装,侧重理解如何实现关键字驱动)
关键字驱动用例的excel文件:

关键字驱动的关键执行方法:
- '''
- 关键字驱动
- '''
- import xlrd
- from appium.webdriver import WebElement
- from appium.webdriver.common.appiumby import AppiumBy
- from appium.webdriver.webdriver import WebDriver
-
- from util.assertUtil import AssertUtil
-
-
- class Context():
- pass
- def runexcel_case(driver: WebDriver,casename):
- file = xlrd.open_workbook(filename = 'E:/pythonProjects/appproject/device.xlsx')
- sheet = file.sheet_by_name(casename)
- case_key = sheet.row_values(0)
- rownum = sheet.nrows
- case=[]
- #获取所有用例步骤
- for i in range(1,rownum):
- data = sheet.row_values(i)
- case.append(dict(zip(case_key,data)))
- for step in case:
- action_ = step.get('action',None)#如果用step['action'],当step没有action会抛异常,action指操作有定位元素、点击、输入等操作
- find_ = step.get('find', None)# 定位元素,是find_element、还是find_elements
- selector_ = step.get('selector', None)#定位元素的类型
- selector_value_ = step.get('selector_value', None)#定位元素的值
- save_obj_ = step.get('save_object', None)#将操作的结果保存的对象名
- operate_obj_ = step.get('operate_object', None)#对哪个对象进行操作
- inputtext_ = step.get('inputtext', None)#输入操作的驶入内容
- attribute_value_ = step.get('attribute_value', None)#获取对象什么属性
- if action_ == 'find':
- if save_obj_:
- if selector_ and selector_value_:
- # driver.find_element(getattr(AppiumBy,selector_),selector_value_)#使用getattr(AppiumBy,selector_) 将excel定义的ID、XPATH按照字符串取到AppiumBy中对应的定位值
- setattr(Context,save_obj_,driver.__getattribute__(find_)(getattr(AppiumBy,selector_),selector_value_))#driver.__getattribute__(find_)通过对象按照传入的参数,执行对象指定的方法,
- # 如果是find_element就执行find_element,如果是find_elements就执行find_elements,
- #思考: 需要考虑将find查找到的对象用save_object定义的变量名的变量去接收,需要自定义一块空间,然后利用setattribute将值赋给指定的变量
- else:
- raise ValueError('在用例文件{}行缺少定义selector_或selector_value_的值'.format(case.index(step)))
- pass
- else:
- raise ValueError('在用例文件{}行缺少定义save_obj的值'.format(case.index(step)))
- if action_ == 'click':
- if operate_obj_:
- getattr(Context,operate_obj_).__getattribute__(action_)()
- else:
- raise ValueError('在用例文件{}行缺少定义operate_obj_的值'.format(case.index(step)))
- if action_ =='send_keys':
- if operate_obj_:
- if inputtext_:
- getattr(Context, operate_obj_).__getattribute__(action_)(inputtext_)
- else:
- raise ValueError('在用例文件{}行缺少定义inputtext_的值'.format(case.index(step)))
- else:
- raise ValueError('在用例文件{}行缺少定义operate_obj_的值'.format(case.index(step)))
- if action_ =='get_attribute':
- if save_obj_:
- if operate_obj_ :
- if attribute_value_:
- ele_obj = getattr(Context,operate_obj_)
- if isinstance(ele_obj,WebElement):#如果是find_element返回的是单个元素
- setattr(Context,save_obj_,ele_obj.__getattribute__(action_)(attribute_value_))
- if isinstance(ele_obj, list):#如果是find_elements返回的是list
- setattr(Context,save_obj_,[i.__getattribute__(action_)(attribute_value_)for i in ele_obj])
- else:
- raise ValueError('在用例文件{}行缺少定义attribute_value_的值'.format(case.index(step)))
- else:
- raise ValueError('在用例文件{}行缺少定义operate_obj_的值'.format(case.index(step)))
- pass
- else:
- raise ValueError('在用例文件{}行缺少定义save_obj_的值'.format(case.index(step)))
- if action_ == 'assert':
- assert_type_= step.get('assert_type',None)
- assert_value_ = step.get('assert_value',None)
- expect_value_ = step.get('expect_value',None)
- if assert_value_:
- if str(assert_value_).find('$') == 0:
- # 需要取变量
- assert_value_ = getattr(Context, str(assert_value_).lstrip('$'))
- if str(expect_value_).find('$') == 0:
- expect_value_ = getattr(Context, str(expect_value_).lstrip('$'))
- if assert_type_ and assert_value_:
- if assert_type_ == 'assert_textin':
- AssertUtil.assert_text_in(getattr(Context,assert_value_),expect_value_,'{}不在{}中'.format(expect_value_,assert_value_))
- if assert_type_ == 'assert_equal':
- assert assert_value_ == expect_value_,"实际值{}与期望值{}不相等".format(assert_value_,expect_value_)
- if assert_type_ == 'assert_not_none':
- assert assert_value_ ,'期望值{}是None'.find(assert_value_)
- else:
- raise ValueError('在用例文件{}行缺少定义assert_type_的值'.format(case.index(step)))
testcase文件:
excel中每一个用例是一个sheet页,sheet页名称以test开头,这样可以使用pytest数据驱动执行用例
读取到excel文件的sheet名列表,收集所有以test开头的sheet名存为list,然后数据驱动执行用例
- sheetnames = xlrd.open_workbook(filename='./device.xlsx').sheet_names()
- case_list = [i for i in sheetnames if i.startswith('test')]
- @pytest.mark.parametrize('casename',case_list)
- def test(self,casename):
- runexcel_case(self.driver,casename)