执行视频:https://live.csdn.net/v/216708
源码见:https://blog.csdn.net/qq_42846555/article/details/126173704
├─app
│ base.apk #测试APP
│
├─base
│ │ base_page.py #封装基本base_pege类
│ │ __init__.py
│
├─business
│ │ __init__.py
│ │
│ ├─tiku
│ │ │ tiku_assert.py #tiku,模块断言
│ │ │ tiku_bs.py #tiku,模块业务逻辑
│ │ │ tiku_page.py #tiku,模块业务基本元素
│ │ │ __init__.py
│ │ │
│ │
│
├─common
│ │ all_path.py #路径设置
│ │ app_info.py #获取APP属性信息包名,等等
│ │ get_devices.py #adb 获取设备列表,等 adb 实现的其他功能
│ │ logger.py #打印日志
│ │ readCofig.py #读取 Cofig数据
│ │ read_yaml.py #读取 yaml数据
│ │ __init__.py
│ │
│
├─config
│ ├─pre
│ │ data.yml #测试数据
│ │
│ └─test
│ data.yaml #测试数据
│ devices_info_multiple.yaml #多设备交互类用例执行配置文件
│ devices_info_one.yaml #单设备执行用例执行配置文件
│ group_data_1.yaml #设备1用例测试数据
│ group_data_2.yaml #设备2用例测试数据
│
├─test_case #交互型测试用例
│ test_file_01.py
│ __init__.py
│
├─test_case_mtbf #非交互型测试用例
│ │ test_001.py
│ │ __init__.py
│
├─log #日志保存目录
├─png #测试截图保存目录
├─screencap #测试录屏保存目录
│
│ conftest.py #pytest框架的conftest文件
│ pytest.ini #pytets框架的标签配置文件
│ requirements.txt #项目依赖
│ run.py #运行入口
#!/user/bin/env python3
# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------
# @File : run.py
# @Time : 2022-04-01 15:05
# @Author : mojin
# @Email : [email protected]
# @Software : PyCharm
#-------------------------------------------------------------------------------
import argparse
parser = argparse.ArgumentParser(description='manual to this script')
parser.add_argument('--string', type=str, default=None)
args = parser.parse_args()
#python3 run.py --string=devices_info.yaml
#接收命令行参数
#args.string
#https://blog.csdn.net/guohewei123/article/details/88602524
import shutil
import pytest,time
import subprocess
from multiprocessing import Pool
from common.get_devices import *
from common.logger import Logger
def run_parallel(devices_info):
# 编写规则
# 编写pytest测试样例非常简单,只需要按照下面的规则:
# 测试文件以test_开头(以_test结尾也可以)
# 测试类必须以Test开头,并且不能带有init方法
# 测试函数以test_开头
# 断言使用基本的assert即可
# pipreqs . --encoding=utf8 --force #导出所需依赖
# pip3 install -r requirements.txt -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
# pip3 install -r requirements.txt -i http://pypi.doubanio.com/simple/ --trusted-host pypi.doubanio.com
# pip install allure-pytest #报错ImportError: cannot import name 'get_testplan'
# pip3 install pytest-rerunfailures
# pip3 install pytest-repeat #失败重跑
#pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
#pytest.main()不带任何参数的main()运行当前目录下所有(test_*.py 和 *_test.py)
# pytest.main():main中传入不同的指令用以执行指定测试用例
# -s: 显示程序中的print/logging输出
# -v: 丰富信息模式, 输出更详细的用例执行信息
# -q: 安静模式, 不输出环境信息
# -k:关键字匹配,用and区分:匹配范围(文件名、类名、函数名) pytest.main (['-k','pp']) #'-k',' test_answer',
# (1)‘-s’:关闭捕捉,输出打印信息。
# (2)‘-v’:用于增长测试用例的冗长。
# (3)‘-k’ :运行包含某个字符串的测试用例。如:pytest - k add XX.py 表示运行XX.py中包含add的测试用例。
# (4)‘q’:减小测试的运行冗长。
# (5)‘-x’:出现一条测试用例失败就退出测试。在调试阶段很是有用,当测试用例失败时,应该先调试经过,而不是继续执行测试用例。函数基础示例
# '-m finished','./test_case','--alluredir', './report/allure', @pytest.mark.finished #用例标记 pytest -m "finished and commit" 测试时pytest -m "finished and commit"
# —allure-severities=blocker,critical用例严重级别执行
# reruns = 5:意思是失败重运行5次
# count = 3:意思是重复执行3次
# --lf 叫--last-failed如果只想运行其中2个failed的和1个error用例,那么可以运行pytest - -lf,这样就只运行非passed的用例
# --ff 叫--failed-first如果想先运行上次失败的,后运行其他通过的用例,那么可以运行pytest - -ff
runtime = time.strftime("%Y%m%d%H%M%S", time.localtime())
# xml_addr='./test_report/%s_report.xml'%runtime
# html_addr='./test_report/%s_report.html'%runtime
xml_addr = './target/test_report/report.xml'
html_addr='./target/test_report/report.html'
Logger.info(devices_info)
#pytest.main([f"--cmdopt={devices_list}",'./test_case_mtbf','-vs','--alluredir', './target/allure-results','--html=%s'%html_addr,'--junitxml=%s'%xml_addr,'--self-contained-html'])
#pytest.main([f"--cmdopt={devices_info}", './test_case','-vs',"--reruns=1",'--ff',"--alluredir", "target/allure-results"])
pytest.main([f"--cmdopt={devices_info}", '%s'%devices_info['test_case_path'],"--reruns=1", '-vs', "--alluredir","target/allure-results"])#"--reruns=1",'--count=10',
allure_html = 'allure generate ./target/allure-results -o ./target/allure-report --clean' # 生成allure的html报告
subprocess.call(allure_html, shell=True) # 生成allure的html报告
# allure_cmd = "allure serve ./target/allure-results"
# subprocess.call(allure_cmd, shell=True) # 启动allure报告
def run(Always='False'):
# Always = "True"长时间循环运行运行;
# 默认Always = "False" 运行一次;
# Always = "5", 运行5次;
# 【注意给带引号,"True","False","5"字符串类型】;
try:
shutil.rmtree("./target") # 删除报告目录,删除后只能看到当前执行后的报告结果,不删除能看到执行的历史执行结果
except:
pass
yaml_info_lsit=["devices_info_one.yaml"]#devices_info_one非交互型用例配置文件,devices_info_multiple.yaml交互型用例配置文件
#yaml_info_lsit=["devices_info_multiple.yaml"]#devices_info_multiple交互型用例配置文件
for yaml_name in yaml_info_lsit:
devices_info=read_yaml_devices_info_list_one(yaml_name)
if Always == "True": # Always==True长时间循环运行
n = 1
while True:
Logger.info('<%s开始运行第“%s”轮测试%s>' % ("=" * 40,n, "=" * 40))
with Pool(len(devices_info)) as pool:
pool.map(run_parallel, (devices_info))
pool.close()
pool.join()
Logger.info('<%s运行第“%s”轮测试结束%s>' % ("=" * 40,n, "=" * 40))
n += 1
elif Always == "False": # Always==False运行一次
Logger.info('<%s开始运行测试%s>' % ("=" * 40, "=" * 40))
with Pool(len(devices_info)) as pool:
pool.map(run_parallel, (devices_info))
pool.close()
pool.join()
Logger.info('<%s运行测试结束%s>' % ("=" * 40, "=" * 40))
elif str.isdigit(str(Always)):#判断输入的值是否为数字
n = 1
while n<=int(Always):
Logger.info('<%s开始运行第“%s”轮测试%s>' % ("=" * 40,n, "=" * 40))
with Pool(len(devices_info)) as pool:
pool.map(run_parallel, (devices_info))
pool.close()
pool.join()
Logger.info('<%s运行第“%s”轮测试结束%s>' % ("=" * 40,n, "=" * 40))
n += 1
else:
Logger.info('“Always”输入的值不符合要求,Always值可以为:True or False or 数字')
# allure_cmd = "allure serve ./target/allure-results"
# subprocess.call(allure_cmd, shell=True) # 启动allure报告
if __name__ == '__main__':
# read_yaml_devices_info_list()读取配置文件设备信息
# #devices_info_list()自动获取设备列表组成项目需要的列表形式(两台设备一组,一组就是一个进程)
#python3 run.py --string=devices_info.yaml
# Logger.info('读取设备配置文件名为:%s'%args.string)
# devices_info = read_yaml_devices_info_list(args.string)#命令栏获取配置文件名称进行获取设备列表
#devices_info = read_yaml_devices_info_list_one(read_yaml_name)#获取设备列表
# Always="True"长时间循环运行运行;默认Always="False"运行一次;Always="5",运行5次【注意给带引号,"True","False","5"字符串类型】
run(Always="False")
#!/user/bin/env python3
# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------
# @File : conftest.py
# @Time : 2022-04-01 14:42
# @Author : mojin
# @Email : [email protected]
# @Software : PyCharm
#-------------------------------------------------------------------------------
from common.read_yaml import ReadYaml
from base.base_page import BasePage
import random
import pytest,time,allure,os
from common.app_info import get_app_name_nopath
from common.logger import Logger
import uiautomator2 as u2
#命令行传参addoption 在contetest.py添加命令行选项,命令行传入参数”—cmdopt“, 用例如果需要用到从命令行传入的参数,就调用cmdopt函数:
def pytest_addoption(parser):
parser.addoption("--cmdopt", action="store", default="device_info", help=None)
@pytest.fixture(scope='session')
def cmdopt(pytestconfig):
# 两种写法
#global parameter_data
parameter_data=eval(pytestconfig.getoption("--cmdopt"))
return parameter_data
# return pytestconfig.option.cmdopt
@pytest.fixture(scope='session')
def group_data(cmdopt):#读取数据源文件
yaml_name =(cmdopt)['group_data'] # 获取yaml文件名称
data = ReadYaml(yaml_name).get_yaml_data() # 获取到该组设备对应的yaml文件数据
return data
# @pytest.fixture(params=group_data)
# def group_data_params(request):
# """用例数据,测试方法参数入参该方法名 cases即可,实现同样的参数化
# 目前来看相较于@pytest.mark.parametrize 更简洁。
# """
# return request.param
# fixture传参
# scope参数有四种,分别是'function','module','class','session',默认为function。
# function:每个test都运行,默认是function的scope
# class:每个class的所有test只运行一次
# module:每个module的所有test只运行一次
# session:每个session只运行一次
@pytest.fixture(scope="session")
def drivers(cmdopt):
global dri,dev
dev = (cmdopt)['devices_list']
dri=[]
for i in dev:
d=u2.connect(i)
d.app_install(get_app_name_nopath())#安装应用
Logger.info(d)
dri.append(d)
return dri,dev
# @pytest.fixture(scope="function")
# def open_app():
# data_Yaml_package = data_Yaml['package']
# Logger.info('<%s开始%s>'%("="*20, "="*20))
# i = 0
# for k, v in data_Yaml_package.items():
# dri[i].screen_on()
# #dri[i].unlock() # 解锁设备
# dri[i].set_fastinput_ime(True) # 启用自动化定制的输入法
# dri[i].implicitly_wait(20) # 设置隐式等待时间,默认20s
# dri[i].watcher.start()
# dri[i].watcher.when("允许").click()
# dri[i].watcher.when("立即开始").click()
# dri[i].watcher.when("仅在使用中允许").click()
# dri[i].watcher.when("确定").click()
# dri[i].watcher.when("取消").click()
#
# dri[i].watcher.when("以上都不是").click()
# Logger.info("设备“%s”,打开“%s”APP" % (dev[i], v))
# dri[i].app_start(v,stop=True)# 指定包名启动APP,启动前先结束应用运行状态
# i += 1
#
# yield
#
# Logger.info('执行完成,10秒后关闭APP')
# time.sleep(10)
#
# #for i in range(len(data_Yaml_package)):
# i=0
# for k, v in data_Yaml_package.items():
# dri[i].app_stop(v)
# Logger.info("设备“%s”,关闭“%s”APP" % (dev[i], v))
# dri[i].set_fastinput_ime(False) # 关闭自动化定制的输入法
# dri[i].watcher.reset()
# #d.screen_off()
# i += 1
#
# Logger.info('<%s结束%s>' %("="*20, "="*20))
#@pytest.fixture(scope="function", autouse=True)#scope参数为默认的function:每个test都运行一次,autouse=True用例函数不用输入调用,默认自动执行
@pytest.fixture(scope="function")#,autouse=True
def open_iphone():
from base.base_page import BasePage
Logger.info('<%s开始%s>'%("="*20, "="*20))
n=0
runtime = time.strftime("%Y%m%d%H%M%S", time.localtime())
screenrecord_name = '%s_' % runtime
screenrecord_name_lsit=[]
for i in dev:
BasePage=BasePage(dri[n])
dri[n].screen_on()
dri[n].set_fastinput_ime(True) # 启用自动化定制的输入法
dri[n].implicitly_wait(20) # 设置隐式等待时间,默认20s
dri[n].watcher.start()
dri[n].watcher.when("允许").click()
dri[n].watcher.when("立即开始").click()
dri[n].watcher.when("仅在使用中允许").click()
dri[n].watcher.when("确定").click()
dri[n].watcher.when("取消").click()
dri[n].watcher.when("以上都不是").click()
Logger.info('“%s”手机唤醒' % i)
#dri[n].screenrecord(('%s%s')%(screenrecord_name,i))
BasePage.screenrecord_start(('%s%s')%(screenrecord_name,i),doc='开始录屏……')
screenrecord_name_lsit.append(('%s%s')%(screenrecord_name,i))
n+=1
yield [screenrecord_name_lsit]
Logger.info('执行完成,10秒后关闭APP')
time.sleep(3)
n = 0
for i in dev:
#dri[n].screen_off()
#dri[n].screenrecord.stop()
dri[n].set_fastinput_ime(False) # 关闭自动化定制的输入法
dri[n].watcher.reset()
#Logger.info('“%s”手机息屏'%i)
#d.screen_off()
#BasePage.screenrecord_sotp( '%s%s'%(screenrecord_name,i),doc='结束录屏……')
n += 1
Logger.info('<%s结束%s>' %("="*20, "="*20))
# setup和teardown操作
# setup,在测试函数或类之前执行,完成准备工作,例如数据库链接、测试数据、打开文件等
# teardown,在测试函数或类之后执行,完成收尾工作,例如断开数据库链接、回收内存资源等
# 备注:也可以通过在fixture函数中通过yield实现setup和teardown功能
def pytest_itemcollected(item):
# item._nodeid = '%s_%s_%s' %(dev[0],dev[1],item . _nodeid)
#runtime = time.strftime("%Y%m%d%H%M%S", time.localtime())
item._nodeid = str(random.randint(1, 1000)) + '_' + item._nodeid
#item._nodeid = str(runtime) + '_' + item._nodeid
#!/user/bin/env python3
# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------
# @File : tiku_page.py
# @Time : 2022-06-01 16:59
# @Author : mojin
# @Email : [email protected]
# @Software : PyCharm
#-------------------------------------------------------------------------------
from common.read_yaml import ReadYaml
data_Yaml = ReadYaml("data.yaml").get_yaml_data() # 读取数据
#data_Yaml_package = data_Yaml['package']
class TiKu_Page():
tv_agree = ("resourceId", "com.gsx.tiku:id/tv_agree") # 同意使用并开始
ShanYanOneKey=("text","其他方式登录")
tv_login_pwd=("resourceId","com.gsx.tiku:id/tv_login_pwd")#切换为密码登录
LoginID=('XPath','//*[@resource-id="com.gsx.tiku:id/layoutContent"]/android.widget.RelativeLayout[1]')#输账号框
LoginPWD=('XPath','//*[@resource-id="com.gsx.tiku:id/layoutContent"]/android.widget.RelativeLayout[2]')#输入密码框
iv_select=("resourceId","com.gsx.tiku:id/iv_select")#勾选用户协议
bt_login = ("resourceId", "com.gsx.tiku:id/bt_login") # 登录按钮
tvName= ("XPath", '//*[@resource-id="com.gsx.tiku:id/recyclerView"]/android.widget.FrameLayout[5]/android.widget.LinearLayout[1]') # 我的
#d.xpath('//*[@resource-id="com.gsx.tiku:id/recyclerView"]/android.widget.FrameLayout[5]/android.widget.LinearLayout[1]')
tvNickName=("resourceId", "com.gsx.tiku:id/tvNickName") # 账户id
#!/user/bin/env python3
# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------
# @File : tiku_bs.py
# @Time : 2022-06-01 16:22
# @Author : mojin
# @Email : [email protected]
# @Software : PyCharm
#-------------------------------------------------------------------------------
from base.base_page import BasePage
import uiautomator2 as u2
from common.logger import Logger
import time
from conftest import *
from business.tiku.tiku_page import TiKu_Page as TK
#搜索页面对象
class TiKu_bs():
def __init__(self,drivers):
Logger.info(drivers)
self.d0=BasePage(drivers[0])
#self.d1 = BasePage(drivers[1]) #多设备交互时使用这个,单设备执行注掉
#self.drivers=drivers
def open_TiKu(self,parameter):
runtime = time.strftime("%Y%m%d%H%M%S", time.localtime())
screenrecord_name='%s_open_TiKu_test'%runtime
#self.d0.screenrecord_start(screenrecord_name,doc='开始录屏……') # pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple
Package_name="com.gsx.tiku"
self.d0.clear(Package_name, doc='清理“%s”缓存数据'%(Package_name))
self.d0.open(Package_name,stop=True,doc='打开“%s”APP'%(Package_name))
self.d0.clicks(*TK.tv_agree, doc='点击切换为密码登录')
if self.d0.locate(*TK.ShanYanOneKey,doc='判断其他登录方式是否存在',number=8,exists_type=2):
self.d0.clicks(*TK.ShanYanOneKey, doc='其他登录方式存在,切换为其他方式登录')
self.d0.clicks(*TK.tv_login_pwd,doc='点击切换为密码登录')
self.d0.input(*TK.LoginID,parameter['id'],doc='输入用户id账户')
self.d0.input(*TK.LoginPWD, parameter['pwd'], doc='输入用户登录密码')
self.d0.clicks(*TK.iv_select, doc='勾选用户协议')
self.d0.clicks(*TK.bt_login, doc='点击登录')
self.d0.clicks(*TK.tvName, doc='点击我的')
#self.d0.screenrecord_sotp( screenrecord_name,doc='结束录屏……')
#!/user/bin/env python3
# -*- coding: utf-8 -*-
#-------------------------------------------------------------------------------
# @File : tiku_assert.py
# @Time : 2022-06-01 17:25
# @Author : mojin
# @Email : [email protected]
# @Software : PyCharm
#-------------------------------------------------------------------------------
from base.base_page import BasePage
from business.tiku.tiku_page import TiKu_Page as TK
from common.logger import Logger
class TiKu_Assert:
def __init__(self, drivers):
#self.driver = driver
self.d0 = BasePage(drivers[0])
def tiku_assert(self, expect_text,screenrecord_name_list):
result=[
self.d0.assert_text(*TK.tvNickName, expect_text[0], screenrecord_name_list[0],doc="断言:登录的账户id和获取的id是否相等" ),
]
#self.d0.screenrecord_sotp(screenrecord_name_list[0], doc='添加录屏')
return False not in result
#!/user/bin/env python3
# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------------
# @File : test_001.py
# @Time : 2022-04-21 12:02
# @Author : mojin
# @Email : [email protected]
# @Software : PyCharm
# -------------------------------------------------------------------------------
import multiprocessing as np
import pytest, allure,os
import json
import time
from business.tiku.tiku_bs import TiKu_bs
from business.tiku.tiku_assert import TiKu_Assert
from common.get_devices import get_devices_app_package
from common.logger import Logger
@allure.epic("TiKu") # 项目名称
@allure.feature("TiKu打开") # 一级标签
@allure.story('TiKu测试') # 二级标签
class Test_Case():
'''
用例级别
#blocker,critical,normal,minor,trivial 阻塞,严重,一般,次要,轻微
@allure.severity装饰器按严重性级别来标记case @allure.severity('minor')
allure对用例的等级划分成五个等级
blocker 阻塞缺陷(功能未实现,无法下一步)
critical 严重缺陷(功能点缺失)
normal 一般缺陷(边界情况,格式错误)
minor 次要缺陷(界面错误与ui需求不符)
trivial 轻微缺陷(必须项无提示,或者提示不规范)
'''
# @pytest.mark.parametrize("case", range(20))#参数化
#@pytest.mark.skip(reason="就是不想执行而已") # 不执行次用例
# @pytest.mark.skipif(condition='1<2', reason='feature not implemented') 条件跳过
# @pytest.mark.run(order=3) 自定义测试顺序,顺序为:1,2,3,无标记,-3,-2,-1
# @pytest.mark.xfail(condition='1==2', reason="The test case") # condition 条件不等判断成功
# @pytest.mark.smoke##运行时加上命令‘-m=smoke’ #@pytest.mark.login # 用例标记
# @pytest.mark.flaky(reruns=2, reruns_delay=10) 失败重跑
#@pytest.mark.repeat(1) # 用例重复测试 次数
# @allure.title('测试') # 三级标签,这是在用例方法上面添加用例标题,但不能引用用例传进来的变量
@allure.severity('blocker') # 用例等级
@allure.issue('https://www.baidu.com/', "BUG号:123") # 问题表识,关联标识已有的问题,可为一个url链接地址
@allure.testcase('https://www.baidu.com/', "用例名:测试字符串相等") # url, name=None 用例标识,关联标识用例,可为一个url链接地址
@allure.description('用例描述……') # 用例描述
@allure.link('https://www.baidu.com/', name="关联的连接") # 关联的连接
@allure.step('test_001')
#@pytest.mark.skip(reason="就是不想执行而已") # 不执行此用例
def test_001(self, group_data):
# screenrecord_name_list=open_iphone[0]
# drivers, div = drivers # 给drivers和div赋值 ['devices_list']
#allure.dynamic.title('测试-%s' % (div)) # 这个是用例方法内部添加用例标题的,可以引用用例传进来的变量参数,在用例标题中显示设备编号
parameter = group_data['test_001']['Test_Case']['test_open_TiKu'] # 读取到test_Call用例源数据
# parameter=data
Logger.info(parameter)
with allure.step('步骤图片'):
allure.attach.file("config/diti (18).jpg", name="需要查看的图片",
attachment_type=allure.attachment_type.JPG) # 测试步骤中添加一张图片或视频
with allure.step('步骤1'):
allure.attach(
json.dumps('这是一个allure报告示例', ensure_ascii=False, indent=4),
"附件内容",
allure.attachment_type.TEXT,
)
with allure.step('资源图'):
allure.attach.file('config/test/group_data_1.yaml', name="插入yaml文件数据",
attachment_type=allure.attachment_type.YAML) # 测试步骤中添加一张图片或视频
allure.attach.file('config/45_.mp4', name="插入MP4文件",
attachment_type=allure.attachment_type.MP4) # 测试步骤中添加一张图片或视频
allure.attach.file('config/allure_3x2_qianqian.mp3', name="插入MP3文件",
attachment_type=allure.attachment_type.MP4) # 测试步骤中添加一张图片或视频
with allure.step('打印json数据'):
allure.attach(
json.dumps(group_data, ensure_ascii=False, indent=4),
"附件内容",
allure.attachment_type.JSON,
)
with allure.step('打印列表数据'):
allure.attach(
json.dumps(parameter, ensure_ascii=False, indent=4),
"附件内容",
allure.attachment_type.JSON,
)
pytest.assume(4==2)
pytest.assume(4 ==4)
@allure.severity('blocker') # 用例等级
@allure.issue('https://www.baidu.com/', "BUG号:123") # 问题表识,关联标识已有的问题,可为一个url链接地址
@allure.testcase('https://www.baidu.com/', "用例名:测试字符串相等") # url, name=None 用例标识,关联标识用例,可为一个url链接地址
@allure.description('用例描述……') # 用例描述
@allure.link('https://www.baidu.com/', name="关联的连接") # 关联的连接
@allure.step('TiKu测试')
#@pytest.mark.skip('跳过')
def test_open_TiKu(self, drivers, group_data, open_iphone):
screenrecord_name_list=open_iphone[0]
drivers, div = drivers # 给drivers和div赋值 ['devices_list']
allure.dynamic.title('登录-%s' % (div)) # 这个是用例方法内部添加用例标题的,可以引用用例传进来的变量参数,在用例标题中显示设备编号
parameter = group_data['test_001']['Test_Case']['test_open_TiKu'] # 读取到test_Call用例源数据
# parameter=data
Logger.info(parameter)
TiKu_bs(drivers).open_TiKu(parameter)
assert TiKu_Assert(drivers).tiku_assert([parameter['id']],screenrecord_name_list)
#
#