APP自动化测试框架pytest+allure+uiautomator2+pom
【裙子:822659419】
源码:https://gitee.com/HP_mojin/20220728_wx
介绍
python+pytest+allure+uiautomator2+pom 做微信呼叫流程的一个测试例子
1.手机A微信呼叫,手机B微信接听,交互场景用例
2.用例场景支持多组数据参数化,做数据驱动测试
3.使用uiautomator2库进行APP自动化测试
4.每条用例可实现录屏
5.每条个步骤可自定义截图
6.记录统计用例测试耗时到表格上
7.可实现多设备并行运行,且每台设备数据独立,互不影响
8.可并行执行相同用例或不同用例,达到缩短测试时间目的,提高测试效率
9.pytest+allure+POM结构
软件架构
python+pytest+allure+uiautomator2+pom,强大的pytest自带丰富多彩的插件库,
再加精美的allure报告和简练的uiautomator2组成一个完美的APP自动化框架。
安装教程
allure_python_commons2.9.45
pytest7.1.2
PyYAML6.0
openpyxl3.0.10
ruamel.base1.0.0
uiautomator22.16.18
使用说明
一、配置参数
- device_info配置
交互用例:两台设备之间有业务联系,比如A设备呼叫B设备,也就是微信呼叫和接听的场景
非交互用例:单台设备执行完成业务场景,比如登录,新增列表,删除列表等等(增删改查)
device_info1:非交互类型用例运行的测试设备信息列表,列表有多个元素[{}{}],每个元素为一个设备配置信息,运行时每个设备是一个单独的进程运行
device_info2:交互类型用例运行的测试设备信息列表,列表有多个元素[{}{}],每个元素为一个设备配置信息,运行时每个设备是一个单独的进程运行
devices_list:设备序列号列表 device_info1中devices_list配置一个设备序列号,device_info1中可以有多组数据,每组设备执行为一个进程可以执行,可并行执行
test_case_path:配置测试用例路径的,每个进程可以配置不同的测试路径,如果配置相同路径则并行执行同一套用例,如配置不
同路径可以并行执行不同用例,达到多设备执行减少测试时间提高测试效率的目录
test_data_yaml:之前测试数据配置的yaml文件,现在换另种方法,暂时无用,以这种思路可以添加其他需要用数据信息
NO:进程标识,在取值时通过这个进程标识分别取不相同的数据值,可实现多设备并行运行,且每台设备数据独立,互不影响 - 用例测试数据配置
1.config/test中创建和用例py文件相同的名称的yaml文件(当然可自定义)如:
test_call_001.yaml
test_SendMessage_001.yaml
Test_SendMessage: #类名
test_SendMessage_001: #用例方法名,test_SendMessage_001下包含两组数据,多组数据进行参数化
- '1':#进程1
name_wx: 'xiao_miaoyu'
set_text: '1'
'2':#进程2
name_wx: 'xiao_miaoyu'
set_text: '2'
- '1':
name_wx: 'xiao_miaoyu'
set_text: '3'
'2':
name_wx: 'xiao_miaoyu'
set_text: '4'
- 测试数据使用
test_data_yaml=ReadYaml(‘test_SendMessage_001.yaml’).get_yaml_data() # 读取数据
@pytest.mark.parametrize(“test_data_list”,test_data_yaml[‘Test_SendMessage’][‘test_SendMessage_001’])#用例参数化引用数据
test_data=test_data_list[devices_info[‘NO’]]# devices_info[‘NO’] #进程标识取对应进程数据
M_bs.send_message(test_data)#传入用例执行
from business.message.message_bs import Message_bs
import allure,pytest
from common.read_yaml import ReadYaml
test_data_yaml=ReadYaml('test_SendMessage_001.yaml').get_yaml_data() # 读取数据
@allure.epic('微信')#项目
@allure.feature('微信消息')#一级标题
# @allure.story('微信呼叫')#二级标题
class Test_SendMessage():
@allure.title('微信发起文字消息')#三级标题
@allure.description('微信发起文字消息,只发不收')
@allure.step('起文字消息')
#@pytest.mark.skip('跳过')
@pytest.mark.parametrize("test_data_list",test_data_yaml['Test_SendMessage']['test_SendMessage_001'])#用例参数化引用数据
def test_SendMessage_001(self,drivers_devices,test_data_list,calculation):
calculation.update({'module_tets': '微信消息' }) #给每个模块的第一个用例(或其中一个用例),添加这个
drivers, devices,devices_info = drivers_devices
allure.dynamic.title('微信发起文字消息%s'%devices)
SM_bs=Message_bs(drivers)
test_data=test_data_list[devices_info['NO']]#进程标识取对应进程数据
SM_bs.send_message(test_data)#传入用例执行
- 两种不同类型用例因为设备信息配置差异,不能再一起执行,所以先执行一个再执行另一个,使用for遍历顺序执行,在run.py文件中配置好设备参数数据,运行run.py文件
device_info1 = [
{"devices_list":['0123456789ABCDEF'],"test_case_path":'./test_case','test_data_yaml':"group_data_1.yaml",'NO':"1"},
{"devices_list":['Q5S5T19423012718'],"test_case_path":'./test_case','test_data_yaml':"group_data_2.yaml",'NO':"2"}
]#非交互用例
device_info2 = [
{"devices_list": ['0123456789ABCDEF', 'Q5S5T19423012718', ], "test_case_path": './test_case_jh', 'test_data_yaml': "group_data_1.yaml",'NO':"1"},
#{"devices_list": ['0123456789ABCDEF', 'Q5S5T19423012718', ], "test_case_path": './test_case_jh','test_data_yaml': "group_data_2.yaml",'NO':"2"},
]#交互用例
device_info_list=[device_info1,device_info2]
for device_info in device_info_list:
with Pool(len(device_info)) as pool:#len(device_info)拿到配置的设备数,实现多进程执行,
pool.map(run, (device_info))
pool.close()
pool.join()
二、功能实现
- 手机A微信呼叫,手机B微信接听,交互场景用例
思路:实例化两个driver self.d0 和self.d1,分布代表不相同的设备
设备1发起呼叫
self.d0.click_elements(*Call_Page.VoiceCall, doc=‘点击语音通话,发起呼叫’,screenshot=2)
设备2接听
self.d1.click_elements(*Call_Page.Answer, doc=‘点击接听’,screenshot=2)
class Call_bs():
def __init__(self,drivers_devices):
drivers, devices, devices_info = drivers_devices
self.d0 = BasePage(drivers[0])
self.d1 = BasePage(drivers[1])
self.device0=devices[0]
self.device1 = devices[1]
def call_technological(self,test_data):
name_wx = test_data['name_wx']#''
self.d0.click_elements(*Call_Page.SearchButton,doc='点击首页消息界面搜索按钮')
self.d0.send_keys(*Call_Page.SearchBox,name_wx,doc='输入微信名称',inputType='send_keys',screenshot=2)
Call_Page.SelectPersonnel[1]="微信号: %s"%name_wx #修改微信名,使用传进来的微信名
self.d0.click_elements(*Call_Page.SelectPersonnel, doc='点击选择搜索的人员')
self.d0.click_elements(*Call_Page.EnterCall, doc='点击右侧的加号+拉起操作菜单')
self.d0.click_elements(*Call_Page.VideoCall, doc='点击视频通话拉起选择音频或视频通话')
self.d0.click_elements(*Call_Page.VoiceCall, doc='点击语音通话,发起呼叫',screenshot=2)
self.d1.click_elements(*Call_Page.Answer, doc='点击接听',screenshot=2)
sleep(10)
self.d1.click_elements(*Call_Page.hang_up, doc='点击挂断',screenshot=2)
sleep(3)
-
每条用例可实现录屏【uiautomator2功能中的录屏功能】
在conftest中fixture的参数scope=‘function’,为每条用例前后置执行,用例执行之前开始录屏,结束后结束录屏安装录屏插件 pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple d.screenrecord(mv_path)#开启录屏 d.screenrecord.stop()#结束录屏
@pytest.fixture(scope='function',autouse=True)
def dri_dev(drivers_devices):
drivers,devices,device_info=drivers_devices
mv_path_list=[]
for d,dev in zip(drivers,devices):
time.sleep(2)
runtime = time.strftime("%Y%m%d%H%M%S", time.localtime())
mv_path = './screen/%s_%s.mp4'%(runtime,dev)
#安装录屏插件
# pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple
try:
d.screenrecord(mv_path)
mv_path_list.append(mv_path)
except Exception as e:
Logger.error('%s设备录屏出错!!(报错:%s)'%(dev,str(e)))
mv_path = './config/example.png'
mv_path_list.append(mv_path)
d.app_start('com.tencent.mm', stop=True, wait=True)
yield
for d,dev,mv_path in zip(drivers,devices,mv_path_list):
d.app_stop('com.tencent.mm')
time.sleep(2)
d.screenrecord.stop()
with allure.step('录屏【%s】:'%dev):
allure.attach.file(mv_path, name='录屏文件', attachment_type=allure.attachment_type.MP4)
allure报告展示
-
总览
-
图表
-
功能-呼叫用例
-
功能-发消息
-
录屏
-
耗时统计