小程序UI自动化测试实践:Minium+PageObject !

小程序架构上分为渲染层逻辑层,尽管各平台的运行环境十分相似,但是还是有些许的区别(如下图),比如说JavaScript 语法和 API 支持不一致,WXSS 渲染表现也有不同,所以不论是手工测试,还是UI自动化测试,都必须要在 iOS 和 Android 上分别检查小程序的真实表现。

图片

由于生态方面的原因,目前可选择的小程序UI自动化框架较少。在框架选取过程中,我考察了Appium、Airtest和Minium三个框架,并将三者做了对比,形成了以下图表:

图片

Appium实现微信小程序自动化测试的手段基本上还是套用针对 Hybrid App 的测试方案,通过定位H5 App资源控件,并结合屏幕坐标的方式来操控小程序的页面元素;网易推出的Airtest则是基于图像识别和Poco控件识别,之前也对此框架做过比较深入的了解,但是和Appium一样,对于小程序自动化测试来说,以上两者无法深入小程序逻辑层,只能作用于渲染层,从另外一个角度来说,这两个框架还属于黑盒自动化测试的范畴。

01\Minium

接下来再介绍一下今天的主角:Minium。它是微信小程序官方推出自动化框架,提供了 Python3 和 JavaScript 版本(后者目前已停止维护,后文中的minium单指Python版本),目前最新的版本为1.0.0b2。minium不仅限于 UI 自动化,它还提供了很多有用的特性,比如说支持调用和 Mock 部分 wx 对象上的接口,支持获取和设置小程序页面数据,支持直接触发小程序元素绑定事件等等。

另外,minium提供一个基于unittest封装好的测试框架,利用这个简单的框架对小程序测试也可以起到事半功倍的效果。有了以上功能,不但可以简化用例的一些前期准备工作,更可以对小程序做更针对和更全面的测试。

minium的下载安装和官方文档,可以在代码库查看。官方文档写的还算较为清晰,除此之外,以下网站在学习过程中也有帮助:

  • 微信开放社区: 一些minium使用方面的问题,可以在右上角搜索 "minium" 寻找答案或发起提问;

  • 微信开发者工具: minium与微信开发者工具强关联,开发调试脚本都需要使用微信开发者工具;

  • Minium Demo: 官方提供的python版本的demo,内容非常简单,可以用来简单熟悉一下框架,若要运行demo需要先下载示例小程序代码;

02\Minium + Page Object

早期 GUI 自动化测试脚本,无论是Selenium还是UFT,脚本通常是由一系列的页面控件的顺序操作组成的,有点像操作级别的“流水账”,这主要体现在以下几个方面:

  • 脚本逻辑层次不够清晰,属于 All-in-one 的风格,既有页面元素的定位查找,又有对元素的操作;

  • 脚本的可读性差,在实际项目中,很难从代码中直观看出到底脚本在操作哪个控件,并且脚本的每一行都直接描述各个页面上的元素操作,无法直观的看出脚本更高层的业务测试流程;

  • 通用步骤会在大量测试脚本中重复出现;

Page Object 就是为了解决以上问题而出现的,它是UI自动化测试项目开发实践的最佳设计模式,采用分层封装的设计思想,不同层关心不同的问题。页面对象层只关心元素定位问题,测试用例只关心测试的数据。通过对界面元素和功能模块的封装减少冗余代码,在后期维护中,若元素定位或功能模块发生变化,只需要调整页面元素或功能模块封装的代码,显著提高测试用例的可维护性。

基于PO模式,小程序UI自动化测试Demo项目的目录结构及说明如下:

图片

  • cases/: 存放业务测试用例;

  • outputs/: Minium测试报告;

  • pages/:页面对象模型;

  • *config.json:Minium项目配置文件;

  • suite.json:Minium测试计划文件;

  • route.py:统一存放小程序页面路由;

  • utils.py:存放一些公共方法;

03\具体代码

下面从具体代码入手,简单讲述一下项目的设计思路。

首先是BasePage,它是页面模型基类,用于封装所有页面公用的方法。

import abc

import time

class BasePage(abc.ABC):

    def __init__(self, mini, route, title=""):self.mini = mini
         self.route = route
         self.title = title 
  def open(self): """跳转到小程序目标页面"""
        self._open(self.route, self.title, open_type='redirect')def check_element(self):
        """页面元素审查在子类中实现此方法时,建议使用Minium框架中提供的断言方法,原因如下:调用 Minium 框架提供的断言方法,会拦截 assert 调用,记录运行时数据和截图,自动在测试报告中生成截图 (需要在配置文件中将 assert_capture 设置为True)但是如果直接assert或使用unittest.TestCase提供的断言,当断言失败时,无法自动生成截图"""
        raise NotImplementedError    def on_page(self, route,title=None, wait_util_page_contain_keys: list = None):       """通过对title和route断言,校验跳转进入的当前页是否符合预期"""
        if wait_util_page_contain_keysis not None and isinstance(wait_util_page_contain_keys, list):self.mini.page.wait_data_contains(wait_util_page_contain_keys)else:
            time.sleep(2)self.mini.assertEqual(self.current_route, route,
                              msg="页面路由不匹配, 预期:{},实际:{}".format(route, self.current_route))  if title:self.mini.assertEqual(self.current_title, title,msg="页面标题不匹配, 预期:{},实际:{}".format(title, self.current_title))  @property
     def current_title(self) -> str:  """获取当前页面 head title, 具体项目具体分析,以下代码仅用于演示"""
        return self.mini.page.get_element("XXXXXX").inner_text  @property
     def current_route(self) -> str:    """获取当前页面route, 具体项目具体分析, 以下代码仅用于演示"""
        return self.mini.app.get_current_page().path     def _open(self, route, title, open_type=None):
        """小程序页面跳转可以使用以下三个方法, 一些区别如下:1.`navigate_to`: 此方法会保留当前页面,并跳转到应用内的某个页面(不能跳到tabbar页面). 小程序中页面栈最多十层, 如果超过十层时,再使用此方法跳转页面, 会抛出以下异常:`minium.framework.exception.MiniAppError: webview count limit exceed`. 因此需要在运行用例后及时清除页面栈;2. `redirect_to`: 关闭当前页面,重定向到应用内的某个页面,使用此方法跳转页面时,会替换页面栈,因此页面栈不会超限,但是也导致不支持页面回退;3. `relaunch`: 关闭所有页面,清空页面栈,打开到应用内的某个页面;"""
        open_type = 'redirect' if open_type is None else open_type       if open_type.lower() == "navigate":self.mini.app.navigate_to(route)elif open_type.lower() == "redirect":self.mini.app.redirect_to(route)else:self.mini.app.relaunch(route)self.on_page(route, title)

具体业务的页面模型对象都需要继承BasePage,以IndexPage为例,代码如下所示:

 

from pages.BasePage import BasePage

from route import XXXXX

class IndexPage(BasePage):

    locators = {"AAA": "view#aaa",  "BBB": "view.bbb>image"}def check_element(self):self.mini.assertTrue(self.mini.page.element_is_exists(IndexPage.locators['AAA']) is True)self.mini.assertTrue(self.mini.page.element_is_exists(IndexPage.locators['BBB']) is True)def click_query_btn(self):self.mini.page.get_element("view", inner_text="xxxx").click()self.on_page(route=XXXXX.XXXX.route, title=XXXXX.XXXX.title)

BaseEntity为测试用例基类,用于统一设置用例准备和清理工作,所有项目的测试用例都继承此类:

 

from pathlib import Path

import minium

class BaseEntity(minium.MiniTest):

    """测试用例基类"""
    @classmethod
    def setUpClass(cls):super(BaseEntity, cls).setUpClass()output_dir = Path(cls.CONFIG.outputs)   if not output_dir.is_dir():output_dir.mkdir()

    @classmethod
    def tearDownClass(cls):super(BaseEntity, cls).tearDownClass()cls.app.go_home()def setUp(self):super(BaseEntity, self).setUp()def tearDown(self):super(BaseEntity, self).tearDown()

cases.Moudle_1.index_test.IndexTest代码内容如下:

 

from cases import BaseEntity

from pages.Moudle_1.IndexPage import IndexPage

from route import XXXXX

class ParkIndexTest(BaseEntity):

    def test_index_page(self):   index_page = IndexPage(self, XXXXX.INDEX.route, XXXXX.INDEX.title) index_page.open()index_page.check_element()index_page.click_query_btn()

总结:

  • 优点:PO模式对页面界面交互细节进行了封装,而测试用例基于页面对象完成具体操作,这样可以使我们的自动化测试脚本案例更关注业务,而非界面细节,提高了测试案例的可读性。

  • 缺点(个人观点):开发和维护页面对象的类(Page Class),是一件很耗费时间和体力的事儿。

    待研究方案:小程序页面对象自动生成,不用再手工维护 Page Class ,只需要提供页面路由,就会自动生成这个页面上控件的定位信息,并自动生成 Page Class;

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走!

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

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

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

相关文章

堆的深度剖析及使用

目录 1.堆的创建1.1初始化1.2销毁 2.堆的使用2.1数据插入2.2堆顶元素2.3数据删除 3.堆的难点3.1向上调整3.1.1视频分析向上调整3.1.2 代码分析 3.2向下调整3.2.1视频分析向下调整3.2.2代码分析 1.堆的创建 堆的物理储存方式其实是一个数组,而逻辑储存方式其实是一个…

Hu矩原理 | cv2中基于Hu矩计算图像轮廓相似度差异的函数cv2.matchShapes【小白记笔记】

Hu 矩(Hu Moments) 是一种用于描述轮廓形状的 不变特征。它基于图像的矩提取,经过数学变换得到 7 个不变矩,这些不变矩在图像 平移、旋转和缩放等几何变换下保持不变,适合用来衡量轮廓或形状的相似度差异。 1、图像矩…

计算无人机俯拍图像的地面采样距离(GSD)矩阵

引言 在无人机遥感、测绘和精细农业等领域,地面采样距离(Ground Sampling Distance,简称 GSD)是一个非常重要的指标。GSD 是指图像中每个像素在地面上实际代表的物理距离,通常以米或厘米为单位。GSD 决定了图像的空间…

浅谈怎样系统的准备前端面试

前言 创业梦碎,回归现实,7 月底毅然裸辞,苦战两个月,拿到了美团和字节跳动的 offer,这算是从业以来第一次真正意义的面试,遇到蛮多问题,比如一开始具体的面试过程我都不懂,基本一直是…

深度学习-74-大语言模型LLM之基于API与llama.cpp启动的模型进行交互

文章目录 1 大模型量化方法1.1 GPTQ(后训练量化)1.2 GGUF(支持CPU)1.3 AWQ(后训练量化)2 llama.cpp2.1 功能2.1.1 Chat(聊天)2.1.2 Completion(补全)2.2 运行开源LLM2.2.1 下载安装llama.cpp2.2.2 下载gguf格式的模型2.2.3 运行大模型3 API访问3.1 调用补全3.2 调用聊天3.3 提取…

sql server 字符集和排序

英文: Latin1_General_CI_AS 中文:Chinese_PRC_CI_AS 影响字符存储,解释用户存在单字节字符类型(char,varchar等)里面的数据 字符排序规则(是否区分大小写等) 中国的用户一定要注意…

【docker】列出与特定镜像名相关的镜像

目录 1. 说明2. 列出所有镜像3. 使用镜像名过滤4. 列出特定标签的镜像5. 结合多个过滤条件6. 使用 JSON 格式和 jq 工具 1. 说明 1.在 Docker 中,如果你想列出与特定镜像名相关的镜像,可以使用 docker images 命令并结合过滤选项(如 --filte…

Elasticsearch 实战应用:开启数据搜索与分析新征程

在当今信息爆炸的时代,高效的数据搜索与分析能力成为众多企业和开发者追求的目标。Elasticsearch 作为一款强大的分布式搜索和分析引擎,正逐渐成为数据处理领域的核心工具之一。在我们的教学过程中,旨在让学生深入理解并熟练掌握 Elasticsear…

Navicat 17 功能简介 | SQL 美化

SQL美化 本期,我们将深入挖掘 Navicat 的实用的SQL代码美化功能。你只需简单地点击“SQL 美化”按钮,即可轻松完成 SQL 的格式化。 随着 17 版本的发布,Navicat 也带来了众多的新特性,包括兼容更多数据库、全新的模型设计、可视化…

2009 ~ 2019 年 408【数据结构】大题解析

2009 年 讲解视频推荐:【BOK408真题讲解-2009年(催更就退网版)】 1. 图的应用(10’) 带权图(权值非负, 表示边连接的两顶点间的距离)的最短路径问题是找出从初始顶点到目标顶点之间…

时空AI赋能低空智能科技创新

随着人工智能技术的不断进步,时空人工智能(Spatio-Temporal AI,简称时空AI)正在逐渐成为推动低空经济发展的新引擎。时空AI结合了地理空间智能、城市空间智能和时空大数据智能,为低空智能科技创新提供了强大的数据支持…

SamOut 任意长度推理空间不变

项目地址 import numpy as np import pandas as pd import torch from tqdm import tqdmfrom infer_model import SamOutdef load_model_and_voc(device"cpu"):voc pd.read_pickle("total_voc.pkl")net SamOut(len(voc["voc"]), 1024 512, 64…

17.springcloud_openfeign之扩展组件一

文章目录 一、前言二、默认约定配置FeignAutoConfigurationCachingCapabilityFeignCachingInvocationHandlerFactoryFeignJacksonConfiguration熔断器配置FeignCircuitBreakerTargeterFeignCircuitBreaker.Builder FeignClientsConfigurationCircuitBreakerFactory 总结 一、前…

Python读取Excel批量写入到PPT生成词卡

一、问题的提出 有网友想把Excel表中的三列数据,分别是:单词、音标和释义分别写入到PPT当中,每一张PPT写一个单词的内容。这种批量操作是python的强项,尤其是在办公领域,它能较好地解放双手,读取Excel表后…

Proteus(8.15)仿真下载安装过程(附详细安装过程图)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、Proteus是什么? 二、下载链接 三、下安装步骤 1.解压,有键管理员运行 2.点击Next,进行下一步 3.勾选I accept…&#…

防止私接小路由器

电脑获取到IP地址不是DHCP服务器的IP地址段,导致整个公司网络瘫痪,这些故障现象通常80%原因是私接小路由器导致的,以下防止私接小路由器措施。 一、交换机配置DHCP Sooping DHCP snooping是一种DHCP安全特性,用于防止非法设备获…

动态导出word文件支持转pdf

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、功能说明二、使用步骤1.controller2.工具类 DocumentUtil 导出样式 前言 提示:这里可以添加本文要记录的大概内容: 例如&#xff…

紧固件设计之——开槽六角头防脱出杆螺栓仿真APP

按照产品形态分类,紧固件通常包括以下12类:螺栓、螺柱、螺钉、螺母、自攻螺钉、木螺钉、垫圈、挡圈、销、铆钉、焊钉、组合件与连接副,是一类用于连接和固定各种构件和零部件的重要机械零件,可确保机械装置或设备结构的牢固和稳定…

【Python装饰器】编写一个装饰器,并将其放到适当的位置,目的是让代码 1 秒钟打印一个结果

import timedef fib():back1, back2 0, 1def func():nonlocal back1, back2back1, back2 back2, back1 back2print(back1, end )return funcdef get_fib(n):f fib()for i in range(n):f()n int(input("请输入需要获取的斐波那契数:"))get_fib(n) imp…

mysql中与并发相关的问题?

今天我们来聊聊 MySQL 中与并发相关的一些问题。作为一名资深 Python 开发工程师,我觉得这些问题不仅关乎数据库的稳定性和数据的一致性,更与我们的代码实现和业务逻辑密切相关。 尤其是在高并发环境下,如何保证数据的一致性,如何…