基于PO模式python+pytest+appium多设备并发执行用例自动化框架

pyAppium

项目介绍

pyAppium是python语言,基于PO模式的pytest、Appium二次封装的Android自动化框架,多进程方式在多台手机上同时执行测试,自动获取已连接设备信息,自动启动多个appium服务,同一套测试用例在不同手机上执行,用例执行失败自动截图、收集报错信息,allure插件生成测试报告

框架目录说明

pyAppium        # 项目根目录
├─app           # 测试APP存放目录
├─common        # 公共模块目录
├─config        # 配置文件目录
├─data          # 测试数据目录
├─outputs       # 测试输出目录
│  ├─logs       # 日志目录
│  ├─picture    # 截图存放目录
│  └─reports    # 测试报告存放目录
├─pageViwe      # PO模式页面封装模块
└─testcase      # 测试用例目录

主要功能

  1. 自动启动appium server和杀掉appium server
  2. 自动获取电脑连接设备信息,如设备版本、设备udid
  3. 自动检测端口是否可用、释放占用端口
  4. 自动获取测试APP应用相关信息,如:appPackage、launchable_activity
  5. 自动安装APP和卸载APP
  6. 测试用例无需配置,自动获取用例执行,测试人员只需编写相关case
  7. 用例执行失败自动截图、收集错误日志信息,自动加到测试报告对应case下面
  8. 启动一次APP,执行所有测试用例,缩短测试用例执行间隔,提高测试执行效率
  9. 多进程方式在多台手机上同时执行测试,大幅提高测试效率

框架使用

测试环境

  • win10 64 pycharm2019
  • python 3.7.0 及以上
  • node v12.16.3
  • appium1.20.2
  • java version "1.8.0_181"
  • Android SDK 百度云分享地址 密码:6666

环境安装

  • pycharm2019 安装

    • 自行百度安装,或者留言联系。
  • python环境

  • 安装包下载:点击去下载 自行选择相应版本

  • 下载后点击exe,根据提示安装即可,不会自行百度

  • 环境检测:在cmd命令终端输入:python
    [图片上传失败...(image-99db20-1625479321330)]

  • node 安装

    • 安装包下载:点击去下载 自行选择相应版本
    • 下载后点击exe,根据提示安装即可,不会自行百度
    • 环境检测:在cmd命令终端输入:node -v
      [图片上传失败...(image-749db2-1625479321330)]
  • jdk 安装

    • 安装包下载:点击去下载 自行选择相应版本
    • 下载后点击exe,根据提示安装即可,不会自行百度
    • 配置环境变量:
      • 新建环境变量:JAVA_HOME 如值为:D:\Java\jdk1.8.0_181\bin
      • 在系统变量 Path 的值的前面加入以下内容:%JAVA_HOME%\bin
    • 环境检测:在cmd命令终端输入:java -version
      java环境检测
  • Android SDK 安装

    • 从上面百度云下载 Android sdk 解压到一个目录,不要包含空格、中文路径
    • 新建环境变量:ANDROID_HOME 值为:F:\Andriod_SDK
    • 在系统变量 Path 的值的前面加入以下内容:%ANDROID_HOME%\platform-tools、%ANDROID_HOME%\tools
    • 环境检测:在cmd命令终端输入:adb version
      Android SDK检测
  • appium 安装

    • 安装 node.js,配置 node.js 的环境变量 已完成

    • 配置国内淘宝源 npm config set registry https://registry.npm.taobao.org

      配置淘宝源

    • 安装指定版本appium :npm install -g appium@1.20.2 等待安装完成

    • 环境检测:在cmd命令终端输入:appium -v

      appium 版本查看

    • 安装appium-doctor: npm install appium-doctor -g 作用:检查appium环境是否完整

      appium-doctor 检查

下载框架源码

1.仓库地址:https://gitee.com/King15800/pyAppium.git
2.下载源码:git clone https://gitee.com/King15800/pyAppium.git
3.使用pycharm 打开源码项目
4.安装项目依赖包:pip install -r requirements.txt

使用说明

  • 启动项目正常运行前提:
    • 有手机正常已经连接电脑
    • 修改 test_login.py文件输入账号信息,本demo基于学科网APP编写
    • 已下载APP,放到项目目录App
  • 启动项目:直接运行main.py文件即可。

关键代码说明

启动入口说明 main.py

  • def run_parallel(device_info) 定义一个pytest 启动入口,根据设备进行启动,一个设备启动一个
def run_parallel(device_info):
    pytest.main([f"--cmdopt={device_info}",
                 "--alluredir", "outputs/reports/data"])
    os.system("allure generate outputs/reports/data -o outputs/reports/html --clean")
  • 根据设备数量,使用进程池创建进程进行测试
    device_lists = get_device_infos() # 获取连接设备信息
    uninstall_app(device_lists)  # 卸载APP
    with Pool(len(device_lists)) as pool:
        pool.map(run_parallel, device_lists)
        pool.close()
        pool.join()
  • 完整代码
# _*_coding:utf-8 _*_
# @Time  :2021/6/21 22:33
# @Author  : king
# @File    :main.py
# @Software  :PyCharm
from multiprocessing import Pool

import os
import pytest
from common.app_info import get_device_infos, uninstall_app
from common.appium_auto_server import close_appium


def run_parallel(device_info):
    pytest.main([f"--cmdopt={device_info}",
                 "--alluredir", "outputs/reports/data"])
    os.system("allure generate outputs/reports/data -o outputs/reports/html --clean")


if __name__ == "__main__":
    device_lists = get_device_infos()
    uninstall_app(device_lists)
    with Pool(len(device_lists)) as pool:
        pool.map(run_parallel, device_lists)
        pool.close()
        pool.join()

driver实现 base_driver.py

class BaseDriver:

    def __init__(self, device_info):
        self.device_info = device_info
        cmd = "start /b appium -a 127.0.0.1 -p {0} -bp {1}".format(self.device_info["server_port"],
                                                                   self.device_info["server_port"] + 1)
        open_appium(cmd, self.device_info["server_port"])

    def base_driver(self, automationName="appium"):
        fp = open(f"{configPath}//caps.yml", encoding='utf-8')
        # 超时时间、是否重置
        desired_caps = yaml.load(fp, Loader=yaml.FullLoader)

        # 设备名称
        desired_caps["deviceName"] = self.device_info['device']

        # 版本信息
        desired_caps["platform_version"] = get_devices_version(desired_caps["deviceName"])

        app_path = os.path.join(appPath, get_app_name(appPath))
        desired_caps['app'] = app_path

        desired_caps['appPackage'] = get_app_package_name()

        desired_caps['appActivity'] = get_app_launchable_activity()

        # udid
        desired_caps["udid"] = self.device_info['device']
        # 系统端口号
        desired_caps["systemPort"] = self.device_info["system_port"]

        desired_caps["automationName"] = automationName

        driver = webdriver.Remote(f"http://127.0.0.1:{self.device_info['server_port']}/wd/hub",
                                  desired_capabilities=desired_caps)
        return driver

核心conftest文件 conftest.py

from common.base_driver import BaseDriver
import pytest
import random

from pageViwe.loginPage import LoginPage

driver = None


def pytest_addoption(parser):
    parser.addoption("--cmdopt", action="store", default="device_info", help=None)


@pytest.fixture(scope='session')
def cmdopt(pytestconfig):
    return pytestconfig.getoption("--cmdopt")


# 定义公共的fixture,根据需要进行修改
@pytest.fixture(scope='session')
def common_driver(cmdopt):
    global driver
    base_driver = BaseDriver(eval(cmdopt))
    driver = base_driver.base_driver()
    lg = LoginPage(driver)
    yield lg
    driver.close_app()
    driver.quit()


@pytest.fixture
def device(cmdopt):
    yield eval(cmdopt)

# 报告中区分多设备执行结果
def pytest_itemcollected(item):
    item._nodeid = str(random.randint(1, 1000)) + '_' + item . _nodeid

执行结果

执行日志

2021-06-24-23-10-15-basePage.py-basePage-find_element-33-INFO-同意协议 页面开始查找元素('id', 'com.xkw.client:id/agree_yes')
2021-06-24-23-10-16-basePage.py-basePage-find_element-36-INFO-同意协议 页面查找元素('id', 'com.xkw.client:id/agree_yes')成功!
2021-06-24-23-10-16-basePage.py-basePage-click_element-61-INFO-同意协议 页面点击元素('id', 'com.xkw.client:id/agree_yes')
2021-06-24-23-10-16-basePage.py-basePage-click_element-63-INFO-同意协议 页面点击元素('id', 'com.xkw.client:id/agree_yes')成功!
2021-06-24-23-10-17-basePage.py-basePage-find_element-36-INFO-同意协议 页面查找元素('id', 'com.xkw.client:id/agree_yes')成功!
2021-06-24-23-10-17-basePage.py-basePage-click_element-61-INFO-同意协议 页面点击元素('id', 'com.xkw.client:id/agree_yes')
2021-06-24-23-10-17-basePage.py-basePage-click_element-63-INFO-同意协议 页面点击元素('id', 'com.xkw.client:id/agree_yes')成功!
2021-06-24-23-10-18-basePage.py-basePage-is_element_exist-105-INFO-发现 页面开始查找元素 ('id', 'com.xkw.client:id/discover_search_box')
2021-06-24-23-10-18-basePage.py-basePage-is_element_exist-108-INFO-发现 页面查找元素 ('id', 'com.xkw.client:id/discover_search_box')成功!
2021-06-24-23-10-18-basePage.py-basePage-get_size-121-INFO-开始获取设备屏幕大小。
2021-06-24-23-10-18-basePage.py-basePage-get_size-125-INFO-获取设备屏幕大小完成。(720, 1280)
2021-06-24-23-10-18-basePage.py-basePage-swipe_to_up-167-INFO-首页 页面开始进行上滑
2021-06-24-23-10-19-basePage.py-basePage-is_element_exist-105-INFO-发现 页面开始查找元素 ('id', 'com.xkw.client:id/discover_search_box')
2021-06-24-23-10-19-basePage.py-basePage-find_element-36-INFO-同意协议 页面查找元素('id', 'com.xkw.client:id/agree_yes')成功!
2021-06-24-23-10-19-basePage.py-basePage-click_element-61-INFO-同意协议 页面点击元素('id', 'com.xkw.client:id/agree_yes')
2021-06-24-23-10-19-basePage.py-basePage-click_element-63-INFO-同意协议 页面点击元素('id', 'com.xkw.client:id/agree_yes')成功!
2021-06-24-23-10-19-basePage.py-basePage-is_element_exist-108-INFO-发现 页面查找元素 ('id', 'com.xkw.client:id/discover_search_box')成功!
2021-06-24-23-10-19-basePage.py-basePage-get_size-121-INFO-开始获取设备屏幕大小。
2021-06-24-23-10-19-basePage.py-basePage-get_size-125-INFO-获取设备屏幕大小完成。(720, 1280)
2021-06-24-23-10-19-basePage.py-basePage-swipe_to_up-167-INFO-首页 页面开始进行上滑
2021-06-24-23-10-20-basePage.py-basePage-swipe_to_up-169-INFO-首页 页面开始上滑完成。

测试报告

  • 执行时启动了3台模拟器,case 标题动态生成,可以自行修改,目前是功能名称+设备udid


    测试用例收集结果

    执行失败自动截图加入到报告

注意点

  • 由于使用pytest的自带日志模块,修改pytest 自带loging文件的写入模式,保证记录完整日志信息,文件路径F:\appium_env\Lib\site-packages\_pytest\logging.py 需要根据自己环境地址进入
  • mode='w' 改成 mode='a' 在文件505行左右
        if log_file:
            self.log_file_handler = logging.FileHandler(
                log_file, mode="a", encoding="UTF-8"
            )

未来规划

  • 集成到Jenkins
  • 不同机器能够采用不同数据
  • 可以对于无线连接手机进行测试
  • appium server远程分布式调用执行

以上为内容纯属个人理解,如有不足,欢迎各位大神指正,转载请注明出处!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容