1. 代码介绍
代码实现
#1、定义装饰2层函数
def screenshot_allure(func):
def get_err_screenshot(self,*args,**kwargs):
#2、定义内部函数,拍图操作
try:
func(self,*args,**kwargs)
except Exception as e:
png = self.driver.get_screenshot_as_png()
name = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
allure.attach(png, name, allure.attachment_type.PNG)
raise e
#3、返回内部函数名称
return get_err_screenshot
使用
@screenshot_allure
def step(self,data,run_case):
tc_id = run_case[TestSteps.STEP_TC_ID]
#获取步骤
steps = data.get_steps_by_tc_id(tc_id)
#allure报告
#feature
allure.dynamic.feature(run_case[TestCases.CASES_NOTE])
#story
allure.dynamic.story(run_case[TestCases.CASES_DESC])
#title
allure.dynamic.title(run_case[CaseData.DATA_CASE_ID]+"-"+run_case[CaseData.DATA_NAME])
for step in steps:
log.debug("执行步骤{}".format(step))
#获取元素信息
elements = step[TestSteps.STEP_ELEMENT_NAME]
element = data.get_elements_by_element(step[TestSteps.STEP_TC_ID], elements)
log.debug("元素信息{}".format(element))
#操作步骤 关键表映射 click_btn
operate = self.get_keyword(step[TestSteps.STEP_OPERATE])
#操作判断,是否存在,不存在不执行步骤
if operate:
# 定义方法参数:字典
param_value = dict()
#根据getattr判断执行哪个方法
action_method = getattr(Action(self.driver),operate)
log.debug("该关键字是{}".format(operate))
#定义具体的参数
by = element[Elements.ELE_BY]
value = element[Elements.ELE_VALUE]
# 1、获取by,value,send_value内容
send_value = step[TestSteps.STEP_DATA]
# 2、send_value内容转换,通过case data数据内容
expect = run_case[CaseData.DATA_EXPECT_RESULT]
param_value["by"] = by
param_value["value"] = value
param_value["expect"] = expect
#判断假如有输入内容 字符转换
if send_value:
data_input = run_case[CaseData.DATA_INPUT]
send = self.str_to_dict(data_input)
param_value["send"] = send[send_value]
#step
with allure.step(step[TestSteps.STEP_NAME]):
action_method(**param_value)
else:
log.error("没有operate信息:{}".format(operate))
2. 代码讲解
装饰器之前,先要了解闭包
2.1 闭包
-
闭包介绍及作用
- 当函数调用完,内部的变量就会销毁
- 闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。
- 如果需要使用函数内的变量,或者在变量的基础上完成相应的操作
- 例如:每次在这个变量的基础上和其它数字进行求和计算
- w
-
构成定义
- 在函数嵌套的前提下,内部函数使用外部函数的变量,并且外部函数返回了内部函数
- 使用外部函数变量的内部函数称为闭包
-
闭包构成条件
- 函数嵌套(函数中嵌套函数)
- 内部函数使用外部函数的变量,也或者是参数
- 外部函数返回了内部函数
-
示例代码
-
# 外部函数out def out(num1): # 内部函数inner def inner(num2): # 内部函数使用了外部函数的变量(num1) result = num1 + num2 print("result:", result) # 外部函数返回了内部函数,返回的内部函数就是闭包 return inner # 创建闭包实例 res = out(10) # 执行闭包 res(2) res(3)
-
执行结果
-
result: 12 result: 13
- 从结果中发现,每次运行都是基于10,相加得到12,13
-
-
2.2 装饰器使用
-
装饰器定义
- 本质是一个闭包函数
- 给已有函数增加额外功能的函数
- 例如:log打印信息
- 特点:
- 不修改已有函数的源代码
- 不修改已有函数的调用方式
- 给已有函数增加额外的功能
-
示例代码
-
# 添加日记打印信息 def log(func): def inner(): print("这是一条日志信息") func() return inner #一个登录测试用例 def test_login(): print("测试登录") # 使用装饰器来装饰函数 test_login = log(test_login) test_login()
- 注意
- 闭包函数有且只有一个参数,必须是函数类型,这样定义的函数才是装饰器
- 注意
-
-
语法糖写法
-
# 添加日记打印信息 def log(func): def inner(): print("这是一条日志信息") func() return inner #一个登录测试用例 @log #test_login = log(test_login) def test_login(): print("测试登录") # 使用装饰器来装饰函数 test_login()
-
@log等同于test_login = log(test_login)
-
-
装饰器编写套路
-
定义外部函数
- def log()
-
定义外部函数-参数(参数为函数类型)
- def log(func)
-
定义内部函数
-
def log(func): def inner(): print("这是一条日志信息")
-
-
内部函数调用参数方法
-
def log(func): def inner(): print("这是一条日志信息") func()
-
-
外部函数return 内部函数对象
-
# 添加日记打印信息 def log(func): def inner(): print("这是一条日志信息") func() return inner
-
-
语法糖调用
-
#一个登录测试用例 @log #test_login = log(test_login) def test_login(): print("测试登录") # 使用装饰器来装饰函数 test_login()
-
2.3 通用装饰器使用
-
装饰无参数的函数
- 参考2.2装饰器使用
-
装饰有参数的函数
-
注意:内部函数inner中的参数要与test_sum函数的参数一致
-
# 输出日志 def log(func): def inner(num1, num2): print("--正在努力计算--") func(num1, num2) return inner # 装饰器装饰函数 @log def test_sum(num1, num2): result = num1 + num2 print(result) test_sum(1, 2)
-
-
装饰带有返回值的函数
-
注意内部函数inner中的func调用要与test_sum一致
-
test_sum中有返回值,故inner中的fund()需要有返回值 ,并且inner要return
-
# 输出日志 def log(func): def inner(num1, num2): print("--正在努力计算--") result = func(num1, num2) return result return inner # 装饰器装饰函数 @log def test_sum(num1, num2): result = num1 + num2 return result result = test_sum(1, 2) print(result)
-
-
装饰带有不定长参数的函数
-
注意:参数可使用位置参数*args、关键字参数**kwargs
-
# 添加输出日志的功能 def log(func): def inner(*args, **kwargs): print("--正在努力计算--") func(*args, **kwargs) return inner # 使用语法糖装饰函数 @log def test_sum(*args, **kwargs): result = 0 for value in args: result += value for value in kwargs.values(): result += value print(result) test_sum(1, 2, value=10)
-
-
通用装饰器-总结
-
# 通用装饰器 def log(func): def inner(*args, **kwargs): print("") return func(*args, **kwargs) return inner
2.4 多个装饰器使用
- 多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程
def decorator1(func):
def wrapper(*args, **kwargs):
print('decorator1')
return func(*args, **kwargs)
return wrapper
def decorator2(func):
def wrapper(*args, **kwargs):
print('decorator2')
return func(*args, **kwargs)
return wrapper
@decorator2
@decorator1
def get(self):
return {'msg': 'get view'}
调用过程是由外到内
结果就是
decorator2
decorator1
2.5 带有参数的装饰器
-
带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数,语法格式: @装饰器(参数,…)
-
由于装饰器只能接口一个参数,并且是函数类型,所以在原基础上,外部增加1个函数来实现
-
示例
-
def log(flag):#新增加函数 #原装饰器函数--------start def decorator(func): def inner(*args,**kwargs): if flag == "+": print("加法") elif flag == "-": print("减法") func(*args,**kwargs) return inner #原装饰器函数-------end return decorator#新增加函数返回装饰器 @log("+") #装饰log开始时,相当于decorator=log("+") ,返回decorator装饰器后相当于 @decorator #@decorator相当于 最开始的装饰器 add = decorator(add),add相当于返回了inner这个内部函数 def add(num1,num2): print(num1+num2) @log("-") def sub(num1,num2): print(num1-num2) add(1,2) sub(2,1)
-
总结:使用带有参数的装饰器,其实是在装饰器外面又包裹了一个函数,使用该函数接收参数,返回是装饰器,因为 @ 符号需要配合装饰器实例使用
2.6 类装饰器
就是通过定义一个类来装饰函数
-
示例
-
class MyDecorator: def __init__(self,func): self.func = func def __call__(self, *args, **kwargs): print("MyDecorator") self.func() @MyDecorator def func_print(): print("func_print") func_print()
-
代码讲解
-
定义func_print函数
-
def func_print(): print("func_print")
-
定义一个类
class MyDecorator: pass
-
使用类来装饰func_print
-
@MyDecorator def func_print(): print("func_print")
-
@MyDecorator相当于 func_print = MyDecorator(func_print),这里会发现MyDecorator类实例化时需要参数,所以在类里面我们需要定义
__init__
函数来实现 -
class MyDecorator: def __init__(self,func): func()
-
但是这样还是有问题,往下看
-
-
运行func_print
-
func_print()
-
运行func_print()相当于
-
func_print = MyDecorator(func_print) func_print()
-
这里会发现func_print其实是个实例,实例一般是调用方法,并不能直接运行,所以我们需要在类里面定义
__call__
函数来实现实例直接运行 -
def __call__(self, *args, **kwargs): print("MyDecorator")
-
并且需要执行func_print(),也就是传入的参数func,所以需要在
__init__
函数里面声明属性 -
class MyDecorator: def __init__(self,func): self.func = func
-
然后在
__call_
函数里面是执行func() -
class MyDecorator: def __init__(self,func): self.func = func def __call__(self, *args, **kwargs): print("MyDecorator") self.func()
-
-
-
2.7 装饰的函数在类里面的调用
-
示例1,直接@log即可
-
def log(func): def inner(*args, **kwargs): print("--日志登录为--") func(*args, **kwargs) return inner class TestLogin: @log def test_run(self): print("test_run")
-
装饰器中需要调用 使用类的属性
-
由于类中的方法使用默认是带self,所以在定义装饰器中,增加self的方式来实现
-
示例
-
class Operate(): def __init__(self,driver): self.driver = driver @screenshot_allure def step(self,data,run_case): tc_id = run_case[TestSteps.STEP_TC_ID] #获取步骤 steps = data.get_steps_by_tc_id(tc_id) #allure报告 #feature allure.dynamic.feature(run_case[TestCases.CASES_NOTE]) #story allure.dynamic.story(run_case[TestCases.CASES_DESC]) #title allure.dynamic.title(run_case[CaseData.DATA_CASE_ID]+"-"+run_case[CaseData.DATA_NAME]) #1、定义装饰2层函数 def screenshot_allure(func): def get_err_screenshot(self,*args,**kwargs): #2、定义内部函数,拍图操作,注意self,这里是一个参数对象 try: func(self,*args,**kwargs) except Exception as e: png = self.driver.get_screenshot_as_png() name = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') allure.attach(png, name, allure.attachment_type.PNG) raise e #3、返回内部函数名称 return get_err_screenshot
-
注意:get_err_screenshot(self,*args,**kwargs):中增加了self这个参数,与类中方法 def step(self,data,run_case):保持一致性
-
2.8 allure
-
动态生成报告相关信息
-
#allure报告 #feature allure.dynamic.feature(run_case[TestCases.CASES_NOTE]) #story allure.dynamic.story(run_case[TestCases.CASES_DESC]) #title allure.dynamic.title(run_case[CaseData.DATA_CASE_ID]+"-"+run_case[CaseData.DATA_NAME])
-
当出错拍的图集成到allure中
-
png = self.driver.get_screenshot_as_png() name = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') allure.attach(png, name, allure.attachment_type.PNG)