Appium-Pytest多开设备运行自动化用例

Windows-appium 环境配置

1. 安装node.js

  • node.js
  • cmd下输入命令 node -v可以查看Node.js 的版本。
  • 安装好Node.js后对其做一些基本设置,均在cmd下输入命令即可

2. 设置缓存
npm config set cache "D:\Program Files\nodejs
3. 设置全局模块存放路径
npm config set prefix "D:\Program Files\nodejs
4. 设置淘宝npm镜像

  • 通过设置npm镜像之后,Node.js的一些库均可以在淘宝npm镜像下下载。也就是说以后的npm命令,我们可以用cnpm命令代替,访问该镜像网站。在部分电脑下,可能还要配置cnpm环境,但也是非常简单。例如我的Node.js 安装在D盘,即可在环境变量中添加D:\Program Files\nodejs\node-global
npm config set registry "https://registry.npm.taobao.org"
npm install -g cnpm --registry=https://registry.npm.taobao.org

5. 安装Android SDK 配置Android sdk环境

6. 安装Appium 运行cmd

  • npm --registry http://registry.cnpmjs.org install -g appium
  • 不指定国内镜像直接安装: npm install -g appium如果报错用以下方式安装:
  • 输入命令:npm install -g cnpm --registry=https://registry.npm.taobao.org
  • 再输入命令:cnpm install -g appium
  • 检验是否成功安装Dos窗口操作命令:appium

7. 安装Appium-doctor

  • appium-doctor可以检测appium整体依赖环境配置情况。在cmd下输入以下命令就可以安装
  • cnpm install appium-doctor -g
  • 安装完appium-doctor环境之后,可以通过 appium-doctor看到如下提示说明整体环境配置成功

8. 配置系统cmd环境变量

  • 将路径C:\Windows\System32 添加至系统变量path中

项目目录结构说明

.
├─Common            ## 公共文件
    │ CheckPort.py           ## 检查Appium端口
    │ StartServer.py         ## 启动Appium Server
    │ AppDriver.py           ##  启用Driver Server
│
└─Config                     ##  配置文件
    │  cap.yaml              ## 启动Appium Server参数配置
    │  root_config.py        ## 系统常量配置
│
└─testcase     ## 测试用例
│
└─conftest.py  ## pytest fixture 文件
│
└─main.py  ## 运行用例文件

CheckPort.py

socket

socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

import socket
import os


def check_port(host, port):
    """检测指定的端口是否被占用"""
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 创建socket对象
    try:
        # 连接到address处的套接字。一般,address的格式为元组(hostname,port)
        s.connect((host, port))
        s.shutdown(2)
    except OSError:
        print('port %s is available! ' % port)
        return True
    else:
        print('port %s already be in use !' % port)
        return False


def release_port(port):
    """释放指定的端口"""
    cmd_find = 'netstat -aon | findstr {}'.format(port)  # 查找对应端口的pid
    # 返回命令执行后的结果
    result = os.popen(cmd_find).read()
    if str(port) and 'LISTENING' in result:
        # 获取端口对应的pid进程
        i = result.index('LISTENING')
        start = i + len('LISTENING') + 7
        end = result.index('\n')
        pid = result[start:end]
        cmd_kill = 'taskkill -f -pid %s' % pid  # 关闭被占用端口的pid
        os.popen(cmd_kill)
        print(f'释放进程端口:{port}')
    else:
        print('port %s is available !' % port)


if __name__ == '__main__':
    host = '127.0.0.1'
    port = 4723
    release_port(4723)
    release_port(4725)
    if not check_port(host, port):
        print("端口被占用")
        release_port(port)

StartServer.py

subprocess Popen类
用于在一个新的进程中执行一个子程序;即允许你去创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入、标准输出、标准错误以及返回码等。
主要参数说明:

  • args:用于指定进程的可执行文件及其参数。如果是一个序列类型参数,则序列的第一个元素通常都必须是一个可执行文件的路径。当然也可以使用executeable参数来指定可执行文件的路径。
  • stdin,stdout,stderr:分别表示程序的标准输入、标准输出、标准错误。有效的值可以是PIPE,存在的文件描述符,存在的文件对象或None,如果为None需从父进程继承过来,stdout可以是PIPE,表示对子进程创建一个管道,stderr可以是STDOUT,表示标准错误数据应该从应用程序中捕获并作为标准输出流stdout的文件句柄。
  • shell:如果这个参数被设置为True,程序将通过shell来执行。
  • env:它描述的是子进程的环境变量。如果为None,子进程的环境变量将从父进程继承而来。
import subprocess
import time
from config.root_config import LOG_DIR


def appium_start(host, port, log_name):
    """
    启动appium server
    :param host:
    :param port:
    :param log_name:
    :return:
    """
    # 指定bp端口号
    bootstrap_port = str(port + 1)
    # cmd命令
    cmd = 'start /b appium -a ' + host + ' -p ' + str(port) + ' -bp ' + str(bootstrap_port)
    # 去掉 “/b”,即可以打开cmd弹窗运行
    # cmd = 'start  appium -a ' + host+' -p '+str(port) +' -bp '+ str(bootstrap_port)
    print("%s at %s " % (cmd, time.ctime()))
    # cmd 需要执行的shell命令
    subprocess.Popen(cmd, shell=True, stdout=open(LOG_DIR + '/appium_log/' + f'{log_name}.log', 'w',encoding='utf8'),
                     stderr=subprocess.STDOUT)


if __name__ == '__main__':
    host = '127.0.0.1'
    port = 4723

conftest.py 配置文件

Hook 方法之 pytest_addoption :
pytest_addoption 可以让用户注册一个自定义的命令行参数,方便用户将数据传递给 pytest;
这个 Hook 方法一般和 内置 fixture pytestconfig 配合使用,pytest_addoption 注册命令行参数,pytestconfig 通过配置对象读取参数的值;

parser.addoption() 参数说明:

  • name:自定义命令行参数的名字,可以是:"foo", "-foo" 或 "--foo";
  • action:在命令行中遇到此参数时要采取的基本操作类型;
  • default:如果参数不在命令行中,则生成的默认值。
  • help:对参数作用的简要说明;
import time
import pytest
from Common.app_driver import BaseDriver
from Common.check_port import release_port

base_driver = None

# 注册自定义参数cmdopt到配置对象
def pytest_addoption(parser):
    parser.addoption("--cmdopt", action="store", default="device_info", help=None)

# 从配置对象获取cmdopt的值
@pytest.fixture(scope="session")
def cmd_opt(request):
    return request.config.getoption("--cmdopt")

# 全局启动appium driver
@pytest.fixture(scope="session")
def common_driver(cmd_opt):
    cmd_opt = eval(cmd_opt)
    global base_driver
    base_driver = BaseDriver(cmd_opt)
    time.sleep(1)
    driver = base_driver.get_base_driver()
    driver.implicitly_wait(10)
    yield driver
    driver.quit() # 退出driver
    release_port(cmd_opt["server_port"]) # 释放端口

AppDriver.py

import yaml
from appium import webdriver
from Common.check_port import check_port, release_port
from Common.start_server import appium_start
from config.root_config import CONFIG_PATH


class BaseDriver(object):

    def __init__(self, device_info):
        self.device_info = device_info
        self.host = self.device_info["server_host"]
        self.port = int(self.device_info["server_port"])
        if not check_port(self.host, self.port):
            release_port(self.port)
        appium_start(self.host, self.port, self.device_info['title'])
        with open(CONFIG_PATH, 'r') as f:
            self.data = yaml.load(f, Loader=yaml.FullLoader)

    def get_base_driver(self):
        """获取driver"""
        caps = self.data[self.device_info['title']]
        # 启用webdriver服务
        driver = webdriver.Remote(
            command_executor='http://' + self.host + ':' + str(self.port) + '/wd/hub',
            desired_capabilities=caps # 负责启动服务端时的参数设置
        )
        return driver


if __name__ == '__main__':
    device_info = {"title": "Emulator_two", "server_host": "127.0.0.1", "server_port": "4725", }
    base = BaseDriver(device_info)

main.py

import os
import threading
import time
from multiprocessing import Pool
import pytest
from config.root_config import ROOT_DIR

"""多设备启动文件"""
# appium 端口
device_infos = [
    {"title": "Emulator_one", "server_host": "127.0.0.1", "server_port": "4723", },
    {"title": "Emulator_two", "server_host": "127.0.0.1", "server_port": "4725", }
]
detail_report_path = ROOT_DIR + "\\report\\html"
xml_report_path = ROOT_DIR + "\\report\\xml"


def main(device_info):
    pytest.main(["--cmdopt={}".format(device_info),
                 "--alluredir", xml_report_path, "-vs"])
    os.system("allure generate %s -o %s --clean" % (xml_report_path, detail_report_path))


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

推荐阅读更多精彩内容