使用 Pytest 运行 yaml 文件来驱动 Appium 自动化测试

目录

前言:

获取 yaml 文件

YamlTest 测试类

Appium 初始化

Pytest 测试类

自定义 runtest demo:

自定义错误输出

Yaml 使用方式规则


前言:

使用Pytest来运行yaml文件来驱动Appium自动化测试是一种方便且灵活的方法。通过将测试数据和测试逻辑分离,您可以更轻松地管理和扩展测试用例。

本文主要介绍一下 pytest hook 方法是怎么运行 yaml 文件做测试的 (喜欢其他方式也可以用 xlsx,sql 等),怎么与 Appium 结合,来驱动 Appium 做自动化测试。

该运行方式参照 pytest --doctest 参数运行的方法, 想深入了解的同学可以查看 _pytest/doctest.py 源码

获取 yaml 文件

使用 pytest_collect_file 钩子函数,在脚本运行之前过滤 .yml 文件

def pytest_collect_file(parent, path):
    # 获取文件.yml 文件
    if path.ext == ".yml" and path.basename.startswith("test"):
        return YamlFile(path, parent)

读取 yml 转成 json 移交至 YamlTest 类

class YamlFile(pytest.File):
    # 读取文件内容
    def collect(self):
        import yaml
        raw = yaml.safe_load(self.fspath.open(encoding='utf-8'))
        for name, values in raw.items():
            yield YamlTest(name, self, values)

YamlTest 测试类

下面就是测试类了,这里我们 Appium 还是使用单例初始化,方便所有测试用例继承一个 session

Appium 初始化

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

Pytest 测试类

测试类初始化,这里要说一下,测试类一定要继承 pytest.Item 方法

class YamlTest(pytest.Item):
    def __init__(self, name, parent, values):
        super(YamlTest, self).__init__(name, parent)
        self.values = values
        self.Action = DriverClient().Action # 初始化 Appium
        self.locator = None

为了减少代码的逻辑,取出来的 yaml json 字符串怎么可以直接转化成可运行方法呢?

这里就要说到 class 的 _getattribute_ 内建属性的用法,下面举个简单例子

class TestExample:
    def test1(self):
        print('test1')

>>> TestExample().__getattribute__('test1')()
test1

现在我们就能直接读取 yaml 文件中的 method 字符串直接转化成 Appium api 运行了(method 对应自己封装或 Appium api 的方法)

自定义 runtest demo:

class YamlTest(pytest.Item):
    def __init__(self, name, parent, values):
        super(YamlTest, self).__init__(name, parent)
        self.values = values
        self.Action = DriverClient().Action
        self.locator = None

    def runtest(self):
        # 运行用例
        for self.locator in self.values:
            self.locator['time'] = 5
            if self.locator.get('element'):
                # 需要接收参数
                response = self.Action.__getattribute__(self.locator.get('method'))(self.locator)
            else:
                # 不需要参数
                response = self.Action.__getattribute__(self.locator.get('method'))()
            self.assert_response(response, self.locator)


这里将 Appium api 基本操作封装成了两类:

  • 需要接收元素的参数,例如:点击,查找,输入等
  • 不需要接收元素参数,例如:重启,滑动等

自定义错误输出

def repr_failure(self, excinfo):
    """自定义报错信息,如果没有定义则会默认打印错误堆栈信息,因为比较乱,所以这里自定义一下 """

    if isinstance(excinfo.value, Exception):
        return '测试用例名称:{} \n' \
               '步骤输入参数:{} \n' \
               '数据:{}'.format(self.name, self.locator, excinfo.value.args)

def reportinfo(self):
    return self.fspath, 0, "CaseName: %s" % self.name

下面就是完整的测试类了

class YamlTest(pytest.Item):
    def __init__(self, name, parent, values):
        super(YamlTest, self).__init__(name, parent)
        self.values = values
        self.Action = DriverClient().Action
        self.locator = None

    def runtest(self):
        # 运行用例
        for self.locator in self.values:
            self.locator['time'] = 5
            is_displayed = True
            if not self.locator.get('is_displayed'):
                is_displayed = False if str(self.locator.get('is_displayed')).lower() == 'false' else True
            try:
                if self.locator.get('element'):
                    response = self.Action.__getattribute__(self.locator.get('method'))(yamldict(self.locator))
                else:
                    response = self.Action.__getattribute__(self.locator.get('method'))()
                self.assert_response(response, self.locator)
            except Exception as E:
                if is_displayed:
                    raise E
                pass

    def repr_failure(self, excinfo):
        """自定义报错信息,如果没有定义则会打印堆栈错误信息,调试时可以注释该函数,便于问题查找 """
        if isinstance(excinfo.value, Exception):
            return '测试类名称:{} \n' \
                   '输入参数:{} \n' \
                   '错误信息:{}'.format(self.name, self.locator, excinfo.value.args)

    def assert_response(self, response, locator):
        if locator.get('assert_text'):
            assert locator['assert_text'] in response
        elif locator.get('assert_element'):
            assert response

    def reportinfo(self):
        return self.fspath, 0, "CaseName: %s" % self.name

这里我们主体 Pytest+yaml 测试框架就构建完成了,当然还有各种异常的捕获等钩子函数,自己封装的 Appium api 方法等,上篇文章讲过了,这里就不赘述了,自行选择添加更多功能

Yaml 使用方式规则

因为我们上面将接收方法分成了需要 element 参数和不需要 element 参数两类所以 yaml 格式如下

test_index:
  -
    method: launchApp # 启动 APP
  -
    method: 方法名称 例如:click (必填)
    element: 查找元素id,class等 (选填,配合 method 如需要点击元素,查找元素等必填)
    type: 元素类型 id,xpath,class  name,accessibility id (选填,会自动识别,如识别错误则自行填写)
    name: 测试步骤的名称 例如:点击搜索按钮 (选填)
    text: 需要输入或者查找的文本 (选填,配合部分 method 使用)
    time: 查找该元素需要的时间,默认 5s (选填)
    index: 页面有多个id,class时,不为空则查找元素数组下标 (选填)
    is_displayed: 默认 True ,当为 False 时元素未找到也不会抛异常(选填)

咱们用微博做个 demo 测试一下

test_index:
  -
    method: launchApp # 重启 APP
  -
    method: click
    element: click_ad_skip
    name: 广告跳过按钮
    is_displayed: False
  -
    method: click
    element: 发现
    name: 导航发现按钮
  -
    method: sleep
    element: 3
  -
    method: set_text
    element: com.sina.weibo:id/tv_search_keyword
    text: testerhome
    name: 搜索输入框
  -
    method: set_keycode_enter
  -
    method: screenshot_element
    element: //*[@resource-id="com.sina.weibo:id/lv_content"]/android.widget.RelativeLayout[1]
    name: 搜索内容截图

运行用例

pytest -v ./test_case/test_ranking.yml --alluredir /report/test

或者直接运行文件目录

使用方法和基本 pytest 用法没有太大区别

pytest -v ./test_case --alluredir /report/test

来查看下运行结果:

 

  作为一位过来人也是希望大家少走一些弯路

在这里我给大家分享一些自动化测试前进之路的必须品,希望能对你带来帮助。

(WEB自动化测试、app自动化测试、接口自动化测试、持续集成、自动化测试开发、大厂面试真题、简历模板等等)

相信能使你更好的进步!

点击下方小卡片

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CSS 渐变边框及动画

转载请注明出处,点击此处 查看更多精彩内容 用 CSS 实现渐变边框及动画,下面对关键点进行解释说明,查看完整代码及预览效果请 点击这里。 简单说明原理:使用伪元素 ::before 绘制一个渐变色,然后使用伪元素 ::after 绘…

【数据结构】二叉树详解(2)

⭐️ 前言 ✨ 往期文章链接:二叉树的概念性质 上一篇我们讲了二叉树的结构定义,以及前序/中序/后序的递归遍历,还有一些二叉树的接口实现,本篇我们补充一个二叉树的接口 BinaryTreeDepth。✨上一篇文章链接:二叉树详…

【原创】实现ChatGPT中Transformer模型之Encoder-Decoder

作者:黑夜路人 时间:2023年7月 Transformer Block (通用块)实现 看以上整个链路图,其实我们可以很清晰看到这心其实在Encoder环节里面主要是有几个大环节,每一层主要的核心作用如下: Multi-he…

出租屋智能电表系统

随着科技的不断发展,智能化逐渐成为人们生活中不可或缺的一部分。在房屋租赁市场中,智能电表系统成为越来越多出租屋的标配,为房东和租户带来了便捷和安全。本文将从以下几个方面介绍出租屋智能电表系统的特点和优势。 一、出租屋智能电表系统…

第二十一章:CCNet:Criss-Cross Attention for Semantic Segmentation ——用于语义分割的交叉注意力

0.摘要 全图像依赖关系为视觉理解问题提供了有用的上下文信息。在这项工作中,我们提出了一种称为Criss-Cross Network(CCNet)的方法,以更有效和高效的方式获取这种上下文信息。具体而言,对于每个像素,CCNet…

Linux 系统编程-开发环境(二)

目录 7 压缩包管理 7.1 tar 7.2 rar 7.3 zip 8 进程管理 8.1 who 8.2 ps 8.3 jobs 8.4 fg 8.5 bg 8.6 kill 8.7 env 8.8 top 9 用户管理 9.1 创建用户 9.2 设置用户组 9.3 设置密码 9.4 切换用户 9.5 root用户 9.6 删除用户 10 网络管理 10.1 i…

Word之解决中文和英文混写导致字间距增大的问题(六)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…

机器学习1

核心梯度下降算法: import numpy as np from utils.features import prepare_for_trainingclass LinearRegression:def __init__(self,data,labels,polynomial_degree 0,sinusoid_degree 0,normalize_dataTrue):"""1.对数据进行预处理操作2.先得到…

【iOS】编译与链接

前言 计算机语言分为机器语言、汇编语言和高级语言。 可以将高级语言分为两种:编译语言和解释型语言(直译式语言)。 解释型语言(逐步进行解释执行) 解释语言编写的程序在每次运行时都需要通过解释器对程序进行动态…

(四)「消息队列」之 RabbitMQ 路由(使用 .NET 客户端)

0、引言 先决条件 本教程假设 RabbitMQ 已安装并且正在 本地主机 的标准端口(5672)上运行。如果您使用了不同的主机、端口或凭证,则要求调整连接设置。 获取帮助 如果您在阅读本教程时遇到问题,可以通过邮件列表或者 RabbitMQ 社区…

图数据库:neo4j学习笔记

参考资料:neo4j 教程_w3cschool Springboot集成Neo4j_喝醉的咕咕鸟的博客-CSDN博客 SpringBoot 整合 Neo4j_springboot neo4j_$懒小猿$的博客-CSDN博客 图数据库Neo4j实战(全网最详细教程)_neo4j使用教程_星川皆无恙的博客-CSDN博客 代码片段…

【个人笔记】linux的cd命令与目录结构理解

cd命令 cd(英文全拼:change directory)命令用于改变当前工作目录的命令,切换到指定的路径。 若目录名称省略,则变换至使用者的 home 目录 (也就是刚 login 时所在的目录)。 另外,~ 也表示为 home 目录 的…

flask基本用法小白教程+按钮跳转到指定页面+python和pip安装(后附)

一、flask学习教程: 1.1 基本程序: 大家可以在pycharm中复制如下代码,先感受一下flask的基本用法: 点击链接可进入浏览器查看程序运行的结果,在127.0.0.1:5000后面添上/test1/等设定的文字,可查看不同函…

Flutter:网络图像缓存插件——cached_network_image

前言 为什么要使用这个插件,有什么用呢?毕竟官方提供了Image.network来进行网络图片加载 Image.network和CachedNetworkImage都可以用于在Flutter中加载网络图片,但它们之间有一些区别。 Image.network是Flutter核心库提供的一个构造函数&…

Python教程(4)——Python开发工具PyCharm的下载与安装

PyCharm是一种专业的Python集成开发环境(IDE),由JetBrains公司开发和维护。它提供了丰富的功能和工具,帮助开发人员更高效地编写、调试和测试Python代码。如果是一些大型Python项目强烈推荐用这个来开发。今天我们来介绍一下PyCha…

Microsoft Update Assistant导致 MAC 电脑内存占用过高解决方案

目录 问题: 排查原因: 解决方案: 问题: 一直很苦恼,每次开机隔会发下电脑内存就 100%了,这次找了下原因,也记录下. 排查原因: 通过 mac 自带的活动监视器,发现居然是Microsoft Update Assistant它导致的 解决方案: 那这样就简单了,这个应该是 word,execl 的一个自动更新程序…

融云出海:不止假发出口和四卡四待手机,「非洲市场」的参差与机遇

↑ 点击预约“融云北极星”直播↑ 点击预约“实时社区”直播 比白皮书更精炼省流,比图谱更实用有效。 融云《社交泛娱乐出海作战地图》,被多位大咖标记为出海人必备工作手册。针对地图的核心模块,我们推出了系列解读文章,更详尽…

56 # 实现 pipe 方法进行拷贝

pipe 是异步的,可以实现读一点写一点,管道的优势:不会淹没可用内存,但是在导入的过程中无法获取到内容 const fs require("fs"); const path require("path");fs.createReadStream(path.resolve(__dirname…

苹果平板电容笔好用吗?第三方apple pencil推荐

自从苹果推出了ipad的电容笔之后,一直在市场上保持着十分火爆的热度,但是因为Apple Pencil的价格太高,一般的消费者根本没有足够预算去入手。所以市场上就不断涌现出了不少可以很好代替Apple Pencil的平替电容笔,并且深受人们的热…

Word 插件实现读取excel自动填写

日常工作中碰到需要将EXCEL的对应数据记录填写到word文档对应的位置,人工操作的方式是: 打开exel表—>查找对应报告号的行—>逐列复制excel表列单元格内容到WORD对应的位置(如下图标注所示) 这种方法耗时且容易出错。实际上…