微信小程序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):
    

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

from pages.BasePage import BasePage
from route import XXXXX

class IndexPage(BasePage):
    

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

from pathlib import Path
import minium

class BaseEntity(minium.MiniTest):
   

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

from cases import BaseEntity
from pages.Moudle_1.IndexPage import IndexPage
from route import XXXXX

class ParkIndexTest(BaseEntity):
    

总结:

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

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

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

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

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!


http://www.niftyadmin.cn/n/5149970.html

相关文章

【Android】android studio 怎么下载NDK

序言 新版的android studio在【Project Structure】里面的NDK路径是灰色的,无法点击,导致找不到ndk路径,也无法添加ndk。 下载方法 去这里找,一定要点这个按钮才能出现ndk。 下载之后,要在这个文件里面添加ndk路径

Oracle数据库创建Sequence序列的基本使用

1.作用就是批量插入数据的时候可以给一个主键 sequence dose not exist_sequence not exist_拒—绝的博客-CSDN博客 Oracle创建Sequence序列_TheEzreal的博客-CSDN博客 Oracle序列(sequence)创建失败,无法取值(.nextval&#x…

分糖果,偶数与奇数的问题

描述 阿离和阿信是很好的朋友,他们经常在一起玩好玩的游戏。这一天,他们玩了一个关于糖果的游戏: 有 n 包糖果,每包有 ��ai​ 糖果,如果该包糖果数是偶数,阿离会拿走,否则…

macOS 下 starUML 软件激活方案

starUML每次打开都弹出提示其实挺烦的,于是研究了一下如何 po 解(激活)它。记录一下方法以便以后使用。 我觉得这个软件很好用,大型项目的所有图我都是用这个软件画的。 直接上步骤!先关掉starUML 1、安装 asar,以便可以打开 asa…

IDEA集成Docker插件打包服务镜像与运行【附Docker命令汇总】

Docker官网 Docker官网:https://www.docker.com/ Docker Hub官网:http://hub.docker.com/ 什么是Docker Docker 是一个开源的容器引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者和系统管理员在笔记本上编…

非常有用的工具箱IT-Tools

什么是 IT-Tools ? IT-Tools 汇集了 70 多种对开发人员和 IT 工作人员有用的工具。这个令人惊叹的工具的酷炫之处在于它不需要设置,不需要持久卷,您可以立即开始使用它。它包含大量工具,可生成密码、编辑 CSS 和 HTML 代码、文件格…

代码随想录二刷Day58

583. 两个字符串的删除操作 知道了递推公式挺好写的&#xff0c;和之前那道题差不多 class Solution { public:int minDistance(string word1, string word2) {vector<vector<int>> dp(word1.size() 1, vector<int>(word2.size() 1, 0));for (int i 0; …

mapbox使用marker创建html点位信息

mapbox使用marker创建html点位信息 codePen地址 mapboxgl.accessToken "pk.eyJ1IjoibGl1emhhbzI1ODAiLCJhIjoiY2xmcnV5c2NtMDd4eDNvbmxsbHEwYTMwbCJ9.T0QCxGEJsLWC9ncE1B1rRw"; const center [121.29786, 31.19365]; const map new mapboxgl.Map({container: &quo…