PYTHON+AIRTEST+PYTEST控制多款手机实现APP UI自动化
python+airtest+pytest 实现多款APP UI 自动化
如果代码有什么问题,欢迎指出(毕竟我也是新手)
自从找不到工作,就只能看看APP自动化程序了,增加下知识
壹、原理:
使用poco 库+多进程 连接多款手机进行操作
贰、用到的库:
- poco (airtest的库)
pip install poco
2.pocoui
pip install pocoui
3.airtest
pip install airtest
4.pytest
pip install -U pytest
5.pytest-html(报告模块)
pip3 install pytest-html -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
6.pytest-repeat (重复测试用例的模块)
pip3 install pytest-repeat -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
7.allure (也是报告)
pip3 install allure-pytest -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
叁:文件目录:
![](https://i-blog.csdnimg.cn/blog_migrate/75d5bcac771157312cfa0de843f20258.png)
目录详解(下文没说的文件是不重要的文件)
1.config_dy – config.py 一些文件目录集中在一个文件,方便修改
import os
import datetime
# 项目根目录
BASE_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
# 日志目录
LOG_DIR = os.path.join(BASE_DIR, 'dy_log')
# 截图文件
SCRE_DIR = os.path.join(BASE_DIR, 'until_dy\screen')
#当前时间
NOW_TIME = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S')
# 运行文件
RUN_DIR = os.path.join(BASE_DIR, 'until_dy\pro_main.py')
if __name__ == '__main__':
print(RUN_DIR)
2.config_dy – config.ini 页面元素配置文件
3.config_dy --dy_config 读取ini 配置文件
# -*- coding: gbk -*-
import configparser
import os
class Page_config(object):
def __init__(self):
# os.path.dirname 去掉文件名 返回目录
# os.path.realpath(__file__) 范围当前文件目录包括文件名
BASE_DIR = os.path.dirname(os.path.realpath(__file__))
ZHEN_DIR = os.path.join(BASE_DIR, 'config.ini')
self.file = ZHEN_DIR
self.config = configparser.ConfigParser()
self.config.read(self.file)
# 获得所有section 列表返回
def getAllSections(self):
return self.config.sections()
# 获得所有option 列表返回
def getAllOptions(self, section):
return self.config.options(section)
# 获取指定option 的值, 以字典格式返回
def getOnesection(self,section,option):
try:
locator =self.config.get(section,option)
if option in self.config.options(section):
locator = {option:locator}
# locator = dict(self.config.items())
return locator
except configparser.NoOptionError as e:
return e
# return 'error: No option "{}" in section: "{}"'.format(option, section)
if __name__ == '__main__':
page = Page_config()
print(page.getOnesection('douyin_page','guanzhu'))
4.config_dy --openexcel.py 用来读取本地excel 文件。有很多测试数据就用这个读取
# -*- coding: gbk -*-
# 解析excel
# 获取的数据为 字符串
import xlrd
import os
class OpneExcel(object):
def __init__(self,path):
self.path = path
self.wk = xlrd.open_workbook(self.path) if '测试用例.xlsx' in os.path.basename(self.path) else None
self.data = self.wk.sheet_by_name('Sheet1')
# 获取数据总行数
def getnrows(self):
return self.data.nrows
# 获取数据总列数
def getncols(self):
return self.data.ncols
# 获取指定行 区间数据
# colx索引是从0 开始的
def getnrows_math(self,colx, start, end):
return self.data.col_values(colx , start_rowx=start, end_rowx=end)
# 获取某一列数据
def getonecols(self, colx):
if colx > self.data.ncols:
return '没有那么多列'
else:
return self.data.col_values(colx)
# 获取指定行列对应数据
# 获取数据的索引是从0开始的
def get_ncmath(self,nrows, ncols):
return self.data.cell_value(nrows, ncols)
# 获取多列数据
def getooneclos(self,*colx):
result = []
if len(colx) == 0:
return '空'
else:
for i in colx:
result.append(self.data.col_values(i))
return result
if __name__ == '__main__':
op = OpneExcel('D:\\测试用例.xlsx')
print(op.get_ncmath(1,1))
5.dy_log --loggin_dy 日志文件
import logging
import os
import datetime
from config_dy.conf import *
'''
%(asctime)s 字符串形式的当前时间。
%(name)s Logger的名字
%(levelname)s 文本形式的日志级别
%(message)s 用户输出的消息
'''
class Log(object):
def __init__(self, clevel=logging.INFO):
self.clevel = clevel
self.path = os.path.join(LOG_DIR,"{}.log".format(self.nowtime()))
self.logger = logging.getLogger(__name__)
self.logger.setLevel(level=self.clevel)
self._contorl()
def _contorl(self):
if not self.logger.handlers:
handler = logging.FileHandler(self.path,'a', encoding="utf-8")
handler.setLevel(self.clevel)
self.fromatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(self.fromatter)
self.logger.addHandler(handler)
# 时间
def nowtime(self):
now_time = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S')
return str(now_time)
if __name__ == '__main__':
print(Log()._contorl())
6.until_dy --conftest 全局公用文件(详解会在另外一篇文件中)
特点:
- 共文件内的py 文件调用,解决一个方法重复利用的问题,可以被多个py文件调用
- 运行时,程序先检查是否存在此文件,存在先运行这个文件
- 不需要import,可以直接引用
- 名称是固定的,不能改变
- 文件内报告多个fixture 文件
7.until_dy – connect_iphone.py 用来连接手机
因为用到多进程,此文件已经没有,连接手机已经和多进程卸载一起
from poco.drivers.android.uiautomation import AndroidUiautomationPoco
from airtest.core.api import * # connect_device() start_app() clear_app()
import time
class Open_iphone(object):
def __init__(self):
self.poco = AndroidUiautomationPoco(use_airtest_input=True, screenshot_each_action=False)
def start_app_1(self):
# 链接手机
connect_device('Android:///R28M30RXKNZ')
# 什么都不填写,会默认取当前连接中的第一台手机
connect_device('Android:///')
# 连接本机默认端口连的一台设备号为79d03fa的手机 没试验过
connect_device('Android:///127.0.0.1:5037/79d03fa')
# 连接一个Windows窗口,窗口句柄为123456 没试验过
connect_device('Windows:///123456')
# 启动APP
start_app('App名称')
def clearr_app(self):
clear_app('App名称')
7.until_dy – pro_dong.py 操控手机动作文件
8.until_dy – pro_main.py 测试用例1
9.until_dy --run.py 主要运行文件
文件内容:
- 本来要用队列存放设备号,但是奈何身边只有两个手机,利用率不高,就把设备号放到了公共变量里面(gl._int())
- 用多进程控制多款手机,没用线程,因为线程之间******(一个线程挂了,全都要挂,用不起,用不起),所以没用。
# -*- coding: gbk -*-
import pytest
import os
from until_dy import globalvar as gl
import pytest
from config_dy.conf import *
import subprocess
from airtest.core.api import *
from multiprocessing import Pool
import multiprocessing
import allure
gl._int() # 公共变量,用来存放设备号
q = multiprocessing.Queue()
# 分配adb, 链接手机,放到队列里面,先进先出
def creat_devices():
# 非阻塞行为
devices = subprocess.Popen('adb devices', shell=True, stdout=subprocess.PIPE)
de = []
for i in devices.stdout.readlines()[1:-1]:
de.append(str(i)[2:-5].replace(r'\tdevice',''))
allure.attach(f'设备的参数{de}')
def run_wenjian():
# print('fu',os.getpid())
# print('name{}-{}'.format(name, os.getpid()))
pytest.main(['-s', '-q','pro_main.py', '--alluredir', 'report/xml'])
# pytest.main(["-s", '-v', 'pro_main.py', "--html=report_dy/{}.html".format(os.getpid())
# ])
# 多少个手机 多少个进程,进程池设置为3 USB连接不稳定 '
def make_process():
print('zhu', os.getpid())
print()
pool = Pool(3)
# for i in range(2):
pool.apply_async(run_wenjian)
pool.close()
pool.join()
if __name__ == '__main__':
gl.set_value(creat_devices())
make_process()
版权声明:本文为OYangGouDai原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:python+airtest+pytest控制多款手机实现APP UI自动化_OYangGouDai的博客-CSDN博客_python+airtest