• APP自动化测试框架pytest+allure+uiautomator2+pom


    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
    pytest
    7.1.2
    PyYAML6.0
    openpyxl
    3.0.10
    ruamel.base1.0.0
    uiautomator2
    2.16.18

    使用说明

    一、配置参数
    1. device_info配置
      交互用例:两台设备之间有业务联系,比如A设备呼叫B设备,也就是微信呼叫和接听的场景
      非交互用例:单台设备执行完成业务场景,比如登录,新增列表,删除列表等等(增删改查)
      device_info1:非交互类型用例运行的测试设备信息列表,列表有多个元素[{}{}],每个元素为一个设备配置信息,运行时每个设备是一个单独的进程运行
      device_info2:交互类型用例运行的测试设备信息列表,列表有多个元素[{}{}],每个元素为一个设备配置信息,运行时每个设备是一个单独的进程运行
      devices_list:设备序列号列表 device_info1中devices_list配置一个设备序列号,device_info1中可以有多组数据,每组设备执行为一个进程可以执行,可并行执行
      test_case_path:配置测试用例路径的,每个进程可以配置不同的测试路径,如果配置相同路径则并行执行同一套用例,如配置不
      同路径可以并行执行不同用例,达到多设备执行减少测试时间提高测试效率的目录
      test_data_yaml:之前测试数据配置的yaml文件,现在换另种方法,暂时无用,以这种思路可以添加其他需要用数据信息
      NO:进程标识,在取值时通过这个进程标识分别取不相同的数据值,可实现多设备并行运行,且每台设备数据独立,互不影响
    2. 用例测试数据配置
      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'
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. 测试数据使用
      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)#传入用例执行
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    1. 两种不同类型用例因为设备信息配置差异,不能再一起执行,所以先执行一个再执行另一个,使用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()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    二、功能实现
    1. 手机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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    1. 每条用例可实现录屏【uiautomator2功能中的录屏功能】
      在conftest中fixture的参数scope=‘function’,为每条用例前后置执行,用例执行之前开始录屏,结束后结束录屏

          安装录屏插件
          pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple
          
          d.screenrecord(mv_path)#开启录屏    
          d.screenrecord.stop()#结束录屏    
      
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    @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)
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    1. 记录统计用例测试市场到表格上
      具体详情点击查看https://blog.csdn.net/qq_42846555/article/details/125444604

    allure报告展示

    1. 总览
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DakWAq3c-1659668466496)(./config/png/总览.png)]

    2. 图表
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kauvzrD9-1659668466497)(./config/png/图表.png)]

    3. 功能-呼叫用例
      在这里插入图片描述

    4. 功能-发消息
      在这里插入图片描述

    5. 录屏
      在这里插入图片描述

    6. 耗时统计
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69w1Tmda-1659668466500)(./config/png/耗时统计.png)]

  • 相关阅读:
    注解,自定义注解
    获取URL参数的两种方法及location对象的各项获取方式
    golang中的nil接收器
    第五章——文件内容显示
    学网络光会理论,不会编程?教你快速上手网络套接字编程
    STM32入门100步
    电脑dll丢失应该怎么解决,dll文件丢失怎么恢复方法分享
    LabVIEW中比较两个VI
    算法蓝桥杯-唯一的XX(XX题目)
    【使用jquery编写第一个油猴(tempermonkey)脚本】
  • 原文地址:https://blog.csdn.net/qq_42846555/article/details/126173704