1

微信小程序自动化框架minium实践

 3 years ago
source link: https://tech.youzan.com/wei-xin-xiao-cheng-xu-zi-dong-hua-kuang-jia-shi-jian/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一、背景需求

精选小程序发生了一次线上问题,测试阶段的小程序开发码测试ok,但是小程序正式码由于打包问题,"我的订单"页面文件打包失败,导致线上用户访问我的页面白屏。

当前并不能避免该打包问题,为了规避异常版本发布至线上,需要在预发、体验码发布、正式码发布等各阶段进行主流程回归。手动回归测试非常耗时,在发布前的各阶段,测试人员须重复执行大量测试用例,以确保本次上线功能OK且对其他功能无影响。

一遍又一遍执行相同的测试用例,不仅要花费更多的时间,而且还会降低整体测试效率,因此引入微信小程序自动化以解放重复人力。

1.Jest+小程序SDK

  • 小程序自动化 SDK 为开发者提供了一套通过外部脚本操控小程序的方案,从而实现小程序自动化测试的目的,小程序自动化 SDK 本身不提供测试框架。这意味着你可以将它与市面上流行的任意 Node.js 测试框架结合使用;
  • jest 是facebook推出的一款测试框架,集成了 Mocha,chai,jsdom,覆盖率报告等开发者所需要的所有测试工具,是一款几乎零配置的测试框架;
  • 语言仅支持JavaScript 编写;
  • 使用中遇到问题,网上相关资料比较少;

2.minium框架

  • 微信小程序官方推出的小程序自动化框架,是为小程序专门开发的自动化框架, 提供了 Python 和 JavaScript 版本。
  • 支持一套脚本,iOS & Android & 模拟器,三端运行
  • 提供丰富的页面跳转方式,看不到也能去得到
  • 可以获取和设置小程序页面数据,让测试不止点点点
  • 支持往 AppSerive 注入代码片段
  • 可以使用 minium 来进行函数的 mock, 可以直接跳转到小程序某个页面并设置页面数据, 做针对性的全面测试
  • 暂不支持H5页面的调试;
  • 暂不支持插件内wx接口调用;

精选小程序主要是原生页面,minium和Jest均能满足需求。minium支持Python 和 JavaScript 版本,而且有专门的团队定期维护,遇到问题可以在微信开发者社区进行提问,因此选择了minium。

三、minium介绍

minium提供一个基于unittest封装好的测试框架,利用这个简单的框架对小程序测试可以起到事半功倍的效果。

测试基类Minitest会根据测试配置进行测试,minitest向上继承了unittest.TestCase,并做了以下改动:

  1. 加载读取测试配置
  2. 在合适的时机初始化minium.Minium、minium.App和minium.Native
  3. 根据配置打开IDE,拉起小程序项目和或自动打开真机调试
  4. 拦截assert调用,记录检验结果
  5. 记录运行时数据和截图,用于测试报告生成

使用MiniTest可以大大降低小程序测试成本。

Properties:

名称 类型 默认值 说明

minium.App

App实例,可直接调用minium.App中的方法

minium.Minium

Minium实例,可直接调用minium.Minium中的方法

native

minium.Native

Native实例,可直接调用minium.Native中的方法

代码示例:

#!/usr/bin/env python3
import minium  
class FirstTest(minium.MiniTest):  
    def test_get_system_info(self):
        sys_info = self.mini.get_system_info()
        self.assertIn("SDKVersion", sys_info)
123456

四、环境搭建

  • 安装minium-doc,这个是小程序安装和使用的文档介绍,或者不用自己本地安装直接访问官方文档
  • 安装python 3.8及以上
  • 安装微信开发者工具(我本机使用的版本是1.05.2103200),并打开安全模式: 设置 -> 安全设置 -> 服务端口: 打开
  • 在工具栏菜单中点击设置,选择项目设置,切换到“本地设置”,将调试基础库选择大于2.7.3的库; 开发者工具版本库设置
  • 下载minium安装包并安装,地址参考官网
安装命令:pip3 install minium-latest.zip 或者python3 setup.py install
  • 安装完成后,可执行以下命令查看版本:
minitest -v  
  • 开启微信工具安全设置中的 CLI/HTTP (提供了命令行和HTTP两种调用方式)调用功能。在开发者工具的设置 -> 安全设置中开启服务端口。 开启开发者工具的安全设置
  • 开启微信工具安全设置中的 CLI/HTTP (提供了命令行和HTTP两种调用方式)调用功能。在开发者工具的设置 -> 安全设置中开启服务端口。
  • 开启被测试项目的自动化端口号
"path/to/cli" auto --project "path/to/project" --auto-port 9420

默认的命令行工具所在位置:

macOS: <安装路径>/Contents/MacOS/cli  
Windows: <安装路径>/cli.bat  

五、小程序脚本编写

思路:使用Page Object 架构,使系统架构分层,每一个页面设计为一个Class,包含了页面需要测试的元素,测试用例只要关心测试的数据即可;

1.目录结构

自动化工程目录结构

  • cases/: 存放测试脚本和用例
  • case/base/:页面公共方法
  • case/pages/:页面对象模型
  • outputs/:测试报告
  • test/:测试脚本
  • route.py:小程序页面操作的路径

2.自动化脚本

BasePage是页面基类,封装所有页面会用到的公用方法

class BasePage:  
    def __init__(self, mini):
        self.mini = mini

    def navigate_to_open(self, route):
        """以导航的方式跳转到指定页面,不允许跳转到 tabbar 页面,支持相对路径和绝对路径, 小程序中页面栈最多十层"""
        self.mini.app.navigate_to(route)

    def redirect_to_open(self, route):
        """关闭当前页面,重定向到应用内的某个页面,不允许跳转到 tabbar 页面"""
        self.mini.app.redirect_to(route)

    def switch_tab_open(self, route):
        """跳转到 tabBar 页面,会关闭其他所有非 tabBar 页面"""
        self.mini.app.switch_tab(route)

    @property
    def current_title(self) -> str:
        """获取当前页面 head title, 具体项目具体分析,以下代码仅用于演示"""
        return self.mini.page.get_element("XXXXXX").inner_text

    def current_path(self) -> str:
        """获取当前页面route"""
        return self.mini.page.path
123456789101112131415161718192021222324

HomePage是要测试的精选首页页面

from case.base.basepage import BasePage  
from case.base import route


class HomePage(BasePage):  
    """小程序首页公共方法"""

    locators = {
        "BASE_ELEMENT": "view",
        "BASE_BANNER": "首页banner元素选择器XXX"
    }
    # 首页点击官方补贴的"更多"按钮
    subsidy_more_button = ("跳转页面的元素选择器XXX", "更多")

    """
    校验页面路径
    """
    def check_homepage_path(self):
        self.mini.assertEqual(self.current_path(), route.homepage_route)
    """
    校验页面的基本元素
    """
    def check_homepage_base_element(self):
        # 校验页面是否包含view元素
        self.mini.assertTrue(self.mini.page.element_is_exists(HomePage.locators['BASE_ELEMENT']))
        # 校验页面banner位置
        self.mini.assertTrue(self.mini.page.element_is_exists(HomePage.locators['BASE_BANNER']))
    """
    获取官方补贴,点击"更多"按钮跳转
    """
    def get_subsidy_element(self):
        self.mini.page.get_element(str(self.subsidy_more_button[0]),
                                   inner_text=str(self.subsidy_more_button[1])).click()
123456789101112131415161718192021222324252627282930313233

BaseCase是测试用例基类,用于设置用例输出路径和清理工作,项目的测试用例都继承此类

from pathlib import Path

import minium


class BaseCase(minium.MiniTest):  
    """测试用例基类"""

    @classmethod
    def setUpClass(cls):
        super(BaseCase, cls).setUpClass()
        output_dir = Path(cls.CONFIG.outputs)
        if not output_dir.is_dir():
            output_dir.mkdir()

    @classmethod
    def tearDownClass(cls):
        super(BaseCase, cls).tearDownClass()
        cls.app.go_home()

    def setUp(self):
        super(BaseCase, self).setUp()

    def tearDown(self):
        super(BaseCase, self).tearDown()
12345678910111213141516171819202122232425

3.元素定位的方法

minium 通过 WXSS 选择器来定位元素的,目前小程序仅支持以下的选择器:
常用的元素定位方法

参考例子:

<view id="main" class="page-section page-section-gap" style="text-align: center;"></view>

假如要查找像上面这一个元素的话,他的选择器会像是下面这样:

tageName + #id + .className

view#main.page-section.page-section-gap  
1234567
  • tagName :类型选择器,标签名称,view、checkbox 等等,选择所有指定类型的最简单方式。
  • id:ID 选择器,自定义给元素的唯一 ID,使用时前面跟着 # 号,这是选择单个元素的最有效的方式。
  • className:类选择器,由一个点.以及类后面的类名组成,存在多个类的时候可以以点为间隔一直拼接下

4.编写精选首页的测试用例

被测试的有赞精选小程序首页如下图:

被测试的精选首页 HomePageTest

# coding=utf-8


from case.base import loader  
from case.base.basecase import BaseCase  
from case.pages.homepage import HomePage

"""
小程序首页测试
"""


class HomePageTest(BaseCase):  
    def __init__(self, methodName='runTest'):
        super(HomePageTest, self).__init__(methodName)
        self.homePage = HomePage(self)

    """
     case1:测试首页的跳转路径是否正确,跳转路径要使用绝对路径,小程序默认进入就是首页,所以不用再切换进入的路径
    """

    def test_01_home_page_path(self):
        self.homePage.check_homepage_path()

    """
     case2:页面的基本元素是否存在
    """

    def test_02_page_base_element(self):
        self.homePage.check_homepage_base_element()


    """
    case3:检查首页的"官方补贴"模块存在
    """

    def test_03_live_sale(self):
        self.assertTexts(["官方补贴"], "view")
        self.assertTexts(["轻松赚回早餐钱"], "view")


    """
    case4:从首页点击"更多"跳转到直播特卖页面,页面包含"推荐"模块
    """

    def test_04_open_live_sale(self):
        # 点击首页的"更多"按钮的元素
        self.homePage.get_subsidy_element()
        self.page.wait_for(2)
        result = self.page.wait_for("页面元素选择器xxx")  # 等待页面渲染完成
        if result:
            category = self.page.data['categoryList']
            self.assertEquals("美食", category[0]['title'], "接口返回值包含美食模块")
            self.assertEquals("美妆", category[1]['title'], "接口返回值包含美妆模块")
            self.page.wait_for(2)
            self.app.go_home()


if __name__ == "__main__":  
    loader.run(module="case.homepage_test", config="../config.json", generate_report=True)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960

5.编辑配置文件config.json

{
    "project_path": "XXXXX",
    "dev_tool_path": "/Applications/wechatwebdevtools.app/Contents/MacOS/cli",
    "debug_mode": "debug",
    "test_port": 9420,
    "platform": "ide",
    "app": "wx",
    "assert_capture": false,
    "request_timeout":60,
    "remote_connect_timeout": 300,
    "auto_relaunch": true
}
123456789101112

6.minitest 命令行

minium安装时执行的setup.py文件,指定了minitest命令运行的方法入口为:minium.framework.loader:main
命令行入口 loader.py文件解释了运行的命令行的含义
命令行含义

  • -h, --help: 使用帮助。

  • -v, --version: 查看 minium 的版本。

  • -p PATH/--path PATH: 用例所在的文件夹,默认当前路径。

  • -m MODULEPATH, --module MODULEPATH: 用例的包名或者文件名

  • --case CASENAME: test开头的用例名

  • -s SUITE, --suite SUITE:测试计划文件

  • -c CONFIG, --config CONFIG:配置文件名,配置项目参考配置文件

  • -g, --generate: 生成网页测试报告

  • --modulesearchpath [SYSPATHLIST [SYSPATHLIST ...]]: 添加 module 的搜索路径

  • -a, --accounts: 查看开发者工具当前登录的多账号, 需要通过 9420 端口,以自动化模式打开开发者工具

  • --mode RUN_MODE: 选择以parallel(并行)或者fork(复刻)的方式运行用例

7.suite测试计划文件

{
    "pkg_list": [
      {
        "case_list": [
          "test_*"
        ],
        "pkg": "case.*_test"
      }
    ]
  }
12345678910

suite.json的pkglist字段说明要执行用例的内容和顺序,pkglist 是一个数组,每个数组元素是一个匹配规则,会根据pkg去匹配包名,找到测试类,然后再根据case_list里面的规则去查找测试类的测试用例。可以根据需要编写匹配的粒度。注意匹配规则不是正则表达式,而是通配符。

8.命令行运行脚本

minitest -m case.homepage_test --case test_07_open_live_sale -c config.json -g #运行执行class文件中的指定用例test_07_open_live_sale

minitest -s suite.json -c config.json -g   #按照suite配置去执行用例  

9.生成测试报告

生成报告之后,在对应的目录下面有index.html文件,但是我们不能直接用浏览器打开这个 文件,需要把这个目录放到一个静态服务器上

测试结果存储在outputs下,运行命令python3 -m http.server 12345 -d outputs然后在浏览器上访问http://localhost:12345即可查看报告

六、遇到的问题

1.需要开启被测试小程序应用的自动化测试端口9420
打开自动化端口 开启被测试工程的自动化端口

"path/to/cli" auto --project "path/to/project" --auto-port 9420

2.打开微信开发者工具超时
打开开发者工具超时 微信开发者工具:设置-代理设置,关闭ide的代理 关闭ide的代理

3.连接开发者工具后报错
连接开发者工具报错

原因:可能是微信开发者工具和minium的版本不一致;
我测试使用ok的匹配版本为:
Minium版本:1.0.5  
开发者工具版本:1.05.2102010
python版本:3.8.8  
12345

4.出现以下报错,可能是登陆的开发者工具的账号,没有被测试小程序的开发者权限;
没有被测试小程序的权限

5.运行过程中,发现调用截图的方法比较耗时,但是在config文件设置了"assertcapture": false,配置没生效,仍然会去调用截图的方法;
框架会调用截图方法,配置不生效 ps:猜测是一个bug,然后给微信社区留言了,最新版本1.0.6修复了这个问题
原因:是框架的minitest.py文件调用setup和TearDown方法的时候,没有判断配置文件"assert
capture": false这个条件 minitest截图方法没判断 可以修改minitest.py文件,增加配置文件的判断条件,修改如下:

if self.test_config.assert_capture:

            self.capture("setup")

6.命令行执行的时候加了-p xxx参数,运行时报引入的包不存在
命令行运行报包不存在 原因:命令行运行时默认是当前路径,加-p xxx, 这样会导致脚本运行的PYTHONPATH变了(不是当前目录了),这样会导致包不存在 命令行运行时python路径变了 解决方法:

  • 命令行运行的时候,用-m 指定运行的包路径,不用-p
  • 把-p xxx用到的路径都加入到PYTHONPATH中

七、参考资料

  1. 微信官方文档
  2. 简书上Rethink的相关文章介绍
欢迎关注我们的公众号
coder_qrcode.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK