【自动化测试】Pytest+Appium+Allure 做 UI 自动化的那些事

news2025/1/12 4:00:07

文本主要介绍下 Pytest+Allure+Appium 记录一些过程和经历。

法主要用了啥:

Python3
Appium
Allure-pytest
Pytest

Appium 不常见却好用的方法

Appium 直接执行 adb shell 方法

Appium 启动时增加 --relaxed-security 参数 Appium 即可执行类似adb shell的方法

appium -p 4723 --relaxed-security

使用方法

def adb_shell(self, command, args, includeStderr=False):
“”"
appium --relaxed-security 方式启动
adb_shell(‘ps’,[‘|’,‘grep’,‘android’])

:param command:命令
:param args:参数
:param includeStderr: 为 True 则抛异常
:return:
“”"
result = self.driver.execute_script(‘mobile: shell’, {
‘command’: command,
‘args’: args,
‘includeStderr’: includeStderr,
‘timeout’: 5000
})
return result[‘stdout’]

Appium 直接截取元素图片的方法

element = self.driver.find_element_by_id(‘cn.xxxxxx:id/login_sign’)
pngbyte = element.screenshot_as_png
image_data = BytesIO(pngbyte)
img = Image.open(image_data)
img.save(‘element.png’)

该方式能直接获取到登录按钮区域的截图

Appium 直接获取手机端日志

使用该方法后,手机端 logcat 缓存会清除归零,从新记录

建议每条用例执行完执行一边清理,遇到错误再保存减少陈余 log 输出

Android

logcat = self.driver.get_log(‘logcat’)

iOS 需要安装 brew install libimobiledevice

logcat = self.driver.get_log(‘syslog’)

web 获取控制台日志

logcat = self.driver.get_log(‘browser’)

c = ‘\n’.join([i[‘message’] for i in logcat])
allure.attach(c, ‘APPlog’, allure.attachment_type.TEXT)
#写入到 allure 测试报告中

Appium 直接与设备传输文件

发送文件

#Android
driver.push_file(‘/sdcard/element.png’, source_path=‘D:\works\element.png’)

获取手机文件

png = driver.pull_file(‘/sdcard/element.png’)
with open(‘element.png’, ‘wb’) as png1:
png1.write(base64.b64decode(png))

获取手机文件夹,导出的是zip文件

folder = driver.pull_folder(‘/sdcard/test’)
with open(‘test.zip’, ‘wb’) as folder1:
folder1.write(base64.b64decode(folder))

iOS

需要安装 ifuse

> brew install ifuse 或者 > brew cask install osxfuse 或者 自行搜索安装方式

driver.push_file(‘/Documents/xx/element.png’, source_path=‘D:\works\element.png’)

向 App 沙盒中发送文件

iOS 8.3 之后需要应用开启 UIFileSharingEnabled 权限不然会报错

bundleId = ‘cn.xxx.xxx’ # APP名字
driver.push_file(‘@{bundleId}/Documents/xx/element.png’.format(bundleId=bundleId), source_path=‘D:\works\element.png’)

Pytest 与 Unittest 初始化上的区别

很多人都使用过 unitest 先说一下 pytest 和 unitest 在 Hook method上的一些区别

1.Pytest 与 unitest 类似,有些许区别,以下是 Pytest

class TestExample:
def setup(self):
print(“setup class:TestStuff”)

def teardown(self):
print (“teardown class:TestStuff”)

def setup_class(cls):
print (“setup_class class:%s” % cls.name)

def teardown_class(cls):
print (“teardown_class class:%s” % cls.name)

def setup_method(self, method):
print (“setup_method method:%s” % method.name)

def teardown_method(self, method):
print (“teardown_method method:%s” % method.name)

2.使用 pytest.fixture()
@pytest.fixture()
def driver_setup(request):
request.instance.Action = DriverClient().init_driver(‘android’)
def driver_teardown():
request.instance.Action.quit()
request.addfinalizer(driver_teardown)

初始化实例

1.setup_class 方式调用

class Singleton(object):
“”“单例
ElementActions 为自己封装操作类”“”
Action = None

def new(cls, *args, **kw):
if not hasattr(cls, ‘_instance’):
desired_caps={}
host = “http://localhost:4723/wd/hub”
driver = webdriver.Remote(host, desired_caps)
Action = ElementActions(driver, desired_caps)
orig = super(Singleton, cls)
cls._instance = orig.new(cls, *args, **kw)
cls._instance.Action = Action
return cls._instance

class DriverClient(Singleton):
pass

测试用例中调用

class TestExample:
def setup_class(cls):
cls.Action = DriverClient().Action

def teardown_class(cls):
cls.Action.clear()

def test_demo(self)
self.Action.driver.launch_app()
self.Action.set_text(‘123’)

2.pytest.fixture() 方式调用

class DriverClient():

def init_driver(self,device_name):
desired_caps={}
host = “http://localhost:4723/wd/hub”
driver = webdriver.Remote(host, desired_caps)
Action = ElementActions(driver, desired_caps)
return Action

该函数需要放置在 conftest.py, pytest 运行时会自动拾取

@pytest.fixture()
def driver_setup(request):
request.instance.Action = DriverClient().init_driver()
def driver_teardown():
request.instance.Action.clear()
request.addfinalizer(driver_teardown)

测试用例中调用

#该装饰器会直接引入driver_setup函数
@pytest.mark.usefixtures(‘driver_setup’)
class TestExample:

def test_demo(self):
self.Action.driver.launch_app()
self.Action.set_text(‘123’)

Pytest 参数化方法

1.第一种方法 parametrize 装饰器参数化方法

@pytest.mark.parametrize((‘kewords’), [(u"小明"), (u"小红"), (u"小白")])
def test_kewords(self,kewords):
print(kewords)

多个参数

@pytest.mark.parametrize(“test_input,expected”, [
(“3+5”, 8),
(“2+4”, 6),
(“6*9”, 42),
])
def test_eval(test_input, expected):
assert eval(test_input) == expected

2.第二种方法,使用 pytest hook 批量加参数化

conftest.py

def pytest_generate_tests(metafunc):
“”"
使用 hook 给用例加加上参数
metafunc.cls.params 对应类中的 params 参数

“”"
try:
if metafunc.cls.params and metafunc.function.name in metafunc.cls.params: ## 对应 TestClass params
funcarglist = metafunc.cls.params[metafunc.function.name]
argnames = list(funcarglist[0])
metafunc.parametrize(argnames, [[funcargs[name] for name in argnames] for funcargs in funcarglist])
except AttributeError:
pass

test_demo.py

class TestClass:
“”"
:params 对应 hook 中 metafunc.cls.params
“”"

params = Parameterize(‘TestClass.yaml’).getdata()

params = {
‘test_a’: [{‘a’: 1, ‘b’: 2}, {‘a’: 1, ‘b’: 2}],
‘test_b’: [{‘a’: 1, ‘b’: 2}, {‘a’: 1, ‘b’: 2}],
}
def test_a(self, a, b):
assert a == b
def test_b(self, a, b):
assert a == b

Pytest 用例依赖关系

使用 pytest-dependency 库可以创造依赖关系
当上层用例没通过,后续依赖关系用例将直接跳过,可以跨 Class 类筛选
如果需要跨.py 文件运行 需要将 site-packages/pytest_dependency.py 文件的

class DependencyManager(object):
“”“Dependency manager, stores the results of tests.
“””

ScopeCls = {‘module’:pytest.Module, ‘session’:pytest.Session}

@classmethod
def getManager(cls, item, scope=‘session’): # 这里修改成 session

如果

pip install pytest-dependency

class TestExample(object):

@pytest.mark.dependency()
def test_a(self):
assert False

@pytest.mark.dependency()
def test_b(self):
assert False

@pytest.mark.dependency(depends=[“TestExample::test_a”])
def test_c(self):

TestExample::test_a 没通过则不执行该条用例

可以跨 Class 筛选

print(“Hello I am in test_c”)

@pytest.mark.dependency(depends=[“TestExample::test_a”,“TestExample::test_b”])
def test_d(self):
print(“Hello I am in test_d”)

pytest -v test_demo.py
2 failed

  • test_1.py:6 TestExample.test_a
  • test_1.py:10 TestExample.test_b
    2 skipped

Pytest 自定义标记,执行用例筛选作用

1.使用 @pytest.mark 模块给类或者函数加上标记,用于执行用例时进行筛选

@pytest.mark.webtest
def test_webtest():
pass

@pytest.mark.apitest
class TestExample(object):
def test_a(self):
pass

@pytest.mark.httptest
def test_b(self):
pass

仅执行标记 webtest 的用例

pytest -v -m webtest

Results (0.03s):
1 passed
2 deselected

执行标记多条用例

pytest -v -m “webtest or apitest”

Results (0.05s):
3 passed

仅不执行标记 webtest 的用例

pytest -v -m “not webtest”

Results (0.04s):
2 passed
1 deselected

不执行标记多条用例

pytest -v -m “not webtest and not apitest”

Results (0.02s):
3 deselected

2.根据 test 节点选择用例

pytest -v Test_example.py::TestClass::test_a
pytest -v Test_example.py::TestClass
pytest -v Test_example.py Test_example2.py

3.使用 pytest hook 批量标记用例

conftet.py

def pytest_collection_modifyitems(items):
“”"
获取每个函数名字,当用例中含有该字符则打上标记
“”"
for item in items:
if “http” in item.nodeid:
item.add_marker(pytest.mark.http)
elif “api” in item.nodeid:
item.add_marker(pytest.mark.api)

class TestExample(object):
def test_api_1(self):
pass

def test_api_2(self):
pass

def test_http_1(self):
pass

def test_http_2(self):
pass
def test_demo(self):
pass

仅执行标记 api 的用例

pytest -v -m api
Results (0.03s):
2 passed
3 deselected
可以看到使用批量标记之后,测试用例中只执行了带有 api 的方法

用例错误处理截图,app 日志等

1.第一种使用 python 函数装饰器方法

def monitorapp(function):
“”"
用例装饰器,截图,日志,是否跳过等
获取系统log,Android logcat、ios 使用syslog
“”"

@wraps(function)
def wrapper(self, *args, **kwargs):
try:
allure.dynamic.description(‘用例开始时间:{}’.format(datetime.datetime.now()))
function(self, *args, **kwargs)
self.Action.driver.get_log(‘logcat’)
except Exception as E:
f = self.Action.driver.get_screenshot_as_png()
allure.attach(f, ‘失败截图’, allure.attachment_type.PNG)
logcat = self.Action.driver.get_log(‘logcat’)
c = ‘\n’.join([i[‘message’] for i in logcat])
allure.attach(c, ‘APPlog’, allure.attachment_type.TEXT)
raise E
finally:
if self.Action.get_app_pid() != self.Action.Apppid:
raise Exception(‘设备进程 ID 变化,可能发生崩溃’)
return wrapper

2.第二种使用 pytest hook 方法 (与方法一选一)

@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
Action = DriverClient().Action
outcome = yield
rep = outcome.get_result()
if rep.when == “call” and rep.failed:
f = Action.driver.get_screenshot_as_png()
allure.attach(f, ‘失败截图’, allure.attachment_type.PNG)
logcat = Action.driver.get_log(‘logcat’)
c = ‘\n’.join([i[‘message’] for i in logcat])
allure.attach(c, ‘APPlog’, allure.attachment_type.TEXT)
if Action.get_app_pid() != Action.apppid:
raise Exception(‘设备进程 ID 变化,可能发生崩溃’)

Pytest 另一些 hook 的使用方法

1.自定义 Pytest 参数

pytest -s -all

content of conftest.py

def pytest_addoption(parser):
“”"
自定义参数
“”"
parser.addoption(“–all”, action=“store_true”,default=“type1”,help=“run all combinations”)

def pytest_generate_tests(metafunc):
if ‘param’ in metafunc.fixturenames:
if metafunc.config.option.all: # 这里能获取到自定义参数
paramlist = [1,2,3]
else:
paramlist = [1,2,4]
metafunc.parametrize(“param”,paramlist) # 给用例加参数化

怎么在测试用例中获取自定义参数呢

content of conftest.py

def pytest_addoption(parser):
“”"
自定义参数
“”"
parser.addoption(“–cmdopt”, action=“store_true”,default=“type1”,help=“run all combinations”)

@pytest.fixture
def cmdopt(request):
return request.config.getoption(“–cmdopt”)

test_sample.py 测试用例中使用

def test_sample(cmdopt):
if cmdopt == “type1”:
print(“first”)
elif cmdopt == “type2”:
print(“second”)
assert 1

pytest -q --cmdopt=type2
second
.
1 passed in 0.09 seconds

2.Pytest 过滤测试目录
#过滤 pytest 需要执行的文件夹或者文件名字
def pytest_ignore_collect(path,config):
if ‘logcat’ in path.dirname:
return True #返回 True 则该文件不执行

Pytest 一些常用方法

Pytest 用例优先级(比如优先登录什么的)

pip install pytest-ordering

@pytest.mark.run(order=1)
class TestExample:
def test_a(self):

Pytest 用例失败重试

#原始方法
pytet -s test_demo.py
pytet -s --lf test_demo.py #第二次执行时,只会执行失败的用例
pytet -s --ll test_demo.py #第二次执行时,会执行所有用例,但会优先执行失败用例
#使用第三方插件
pip install pytest-rerunfailures #使用插件
pytest --reruns 2 # 失败case重试两次

Pytest 其他常用参数

pytest --maxfail=10 #失败超过10次则停止运行
pytest -x test_demo.py #出现失败则停止

学习安排上

如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

在这里插入图片描述

视频文档获取方式:

这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片进群即可自行领取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/75038.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

短视频账号搭建之Banner图和视频封面

前面在我赢小禾呈序里学了账号名称、头像和个人简介设置,今天把账号搭建的最后两部分一起公开: banner图是你主页上面的这个主图。 同样它的存在可以有三个作用: 第一个作用比较简单,就是让你的主页更好看。 听起来太简单了&am…

【软件测试】测试人在团队中没地位?怎么办?为什么会出现这样的问题?

目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 为什么会出现这样的…

Linux 学习之:如何让代码在后台保持运行

文章目录nohup 命令使用场景使用方法nohup ... &nohup ... > train.log 2>&1 &结束进程参考文章nohup 命令 使用场景 比如我要在服务器里运行如下代码来训练我的深度学习模型: python train.py但是这样运行你一旦合上笔记本电脑或者换个工作环…

java版商城多商家入驻商城 直播带货商城 电子商务

一个好的SpringCloudSpringBoot b2b2c 电子商务平台涉及哪些技术、运营方案?以下是我结合公司的产品做的总结,希望可以帮助到大家! 搜索体验小程序:海哇 1. 涉及平台 平台管理、商家端(PC端、手机端)、买家…

vxe table 虚拟滚动 表格每一行的高度不一致 出现空白

今天在做表格数据时&#xff0c;发现滚动表格会出现空白区域&#xff0c;如图所示 虚拟滚动表格每一行的高度不一致, 导致表格滚动时出现空白区域 然后在查阅资料时发现有设置:row-config"{height: 70}"这种 &#xff0c;试过发现不行 以下这个不可行 <vxe-grid…

论文常用 | FineBI v6.0 新图表 | 箱形图

箱形图&#xff08;Box-plot&#xff09;又称为盒须图、盒式图或箱线图&#xff0c;是一种用作显示一组数据分散情况资料的统计图&#xff0c;因形状如箱子而得名。在各种领域也经常被使用&#xff0c;常见于品质管理。它主要用于反映原始数据分布的特征&#xff0c;还可以进行…

突破重围,攻“新”为上!凯里亚德与郁锦香酒店以创新势能获投资者青睐

近日&#xff0c;汇聚国内众多投资人的锦江酒店(中国区)品牌沙龙会烟台站顺利举行。本次沙龙活动以“齐风鲁韵 锦绘未来”为主题&#xff0c;锦江酒店(中国区)旗下众多优秀品牌共同亮相。凯里亚德酒店与郁锦香酒店在本次活动中向投资人展示了在如今复杂多变的酒店市场中如何以强…

载波层叠调制在多电平变换器及两电平变换器中的应用

1. 载波层叠调制在MMC中的应用 载波层叠调制在MMC中应用广泛。通过上下桥臂的调制波和多个载波进行比较&#xff0c;得到每个桥臂应该投入的模块数。如下图所示&#xff0c;上下桥臂各有4个模块&#xff0c;每个模块的电容电压是uc&#xff0c;直流侧电压是4uc。A相下桥臂的调制…

Qt 模型视图编程之 ItemDataRole

背景 Qt 中的模型视图架构是用来实现大量数据的存储、处理及其显示的&#xff0c;主要原理是将数据的存储与显示分离&#xff1a;模型定义了标准接口对数据进行访问&#xff1b;视图通过标准接口获取数据并定义显示方式&#xff1b;模型使用信号与槽机制通知视图数据变化。 Q…

C语言百日刷题第十四天

前言 今天是刷题第14天&#xff0c;放弃不难&#xff0c;但坚持一定很酷~ 临近期末&#xff0c;集中把模拟卷的编程题都刷一下 C语言百日刷题第十四天前言模拟题&#xff08;一&#xff09;1.设计程序实现比较两数大小2.排序成绩模拟题&#xff08;二&#xff09;1.求最大值…

力扣(LeetCode)138. 复制带随机指针的链表(C++)

模拟 第一趟遍历&#xff0c;在结点的右侧复制映射。第二趟遍历&#xff0c;复制 randomrandomrandom。第三趟遍历&#xff0c;将链表中的映射结点取出作为新链表。 初始链表如图①。 有必要说明&#xff0c;原结点如 111~555 &#xff0c;映射结点就是 1‘11‘~5‘55‘。 复…

学习笔记--截止12.9 CVAT使用方法、STCN代码使用方法、bitahub使用方法

CVAT使用方法&#xff08;12.5-12.7&#xff09; 对学长来说是一个标注数据集的好工具&#xff0c;但对我来说是个新的知识点 使用这个工具&#xff0c;我们要得到一张有蒙层的图片 然后CVAT的使用方法&#xff08;网上居然没有教程&#xff0c;&#xff0c;&#xff0c;官网的…

金山表单结果如何自动通知至钉钉

金山表单内置了丰富的模版&#xff0c;从表单、接龙、问卷、投票&#xff0c;可以满足你各种表单数据数据收集的需求。但是很多用户经常也会有一个痛点&#xff0c;通过金山表单收集的信息&#xff0c;如何才能实时通知企业微信/钉钉/飞书呢&#xff1f; 比如防疫登记、安全复工…

茶馆无线wifi短信认证方案

茶馆提供公共的无线wifi上网服务&#xff0c;需对用户进行实名认证。手机短信实名认证以其用户体验、综合成本等优势&#xff0c;成为茶馆无线上网认证的首选方案。 一、茶馆如何实现无线wifi短信认证 茶馆要实现访客无线上网短信认证功能&#xff0c;需要借助上网行为管理设备…

SuperMap云套件发布达梦工作空间数据

作者&#xff1a;John GIS云套件和经典版iServer一样可以发布DM数据&#xff0c;同样也和iServer一样需要引入DM依赖包&#xff0c;需要对云套件的ispeco-dashboard-api 和 gisapp-* 镜像进行补充DM依赖包&#xff0c;其补充DM主要有以下几个步骤&#xff0c;本文将通过ispeco-…

DX12_Mesh Shader Instance

之前我们介绍过DX12_Mesh Shaders Render&#xff0c;但是基于MeshShader我们能做的还很多&#xff0c;比如实例化和剔除&#xff08;视锥与遮挡&#xff09;&#xff0c;这也就直接解决了现在主流的GPU-Driven管线方法&#xff0c;是不是一举两得了&#xff08;毕竟MS就是变种…

【产品分析】amazon数据采集亚马逊数据分析评论分析竞品

哈喽&#xff0c;大家好&#xff01;这是写【产品分析】专栏的第三篇文章了&#xff0c;目前国内市场过于饱和&#xff0c;产品竞争压力较大&#xff0c;不少商家开始了海外淘金 采集介绍 通过观察国外平台蓝海关键词&#xff0c;发现Gun这类商品在平台较受欢迎。本文将采集呀…

热门的智慧养老产品有哪些?解读新政策后的超全资料整合

近年来&#xff0c;面对人口老龄化的压力&#xff0c;我国开始着重发展智慧养老。 工信部等部门联合制定了《智慧健康养老产业发展行动计划(2021—2025年)》&#xff0c;针对老年人群多层次、多样化的健康养老需求&#xff0c;重点围绕技术创新应用、加大产品供给、数据平台建…

【数据结构】深度剖析堆排序、TopK问题

文章目录 1、TopK问题 主要思路 程序代码 优越性 2.堆排序 主要思路 程序代码 时间复杂度 堆这个结构实际上还是很有用的&#xff0c;比如TopK问题。 现在有N个数&#xff0c;要求找最大的前K个。很多人会觉得&#xff0c;这不是很容易吗&#xff0c;排序然后取前K个即…

RabbitMQ之发布确认

目录 8.1. 发布确认 springboot 版本 8.1.1. 确认机制方案 8.1.2. 代码架构图 8.1.3. 配置文件 8.1.4. 添加配置类 8.1.5. 消息生产者 8.1.6. 回调接口 8.1.7. 消息消费者 8.1.8. 结果分析 8.2. 回退消息 8.2.1. Mandatory 参数 8.2.2. 消息生产者代码 8.2.3. 回调…