• 基于MiniTest的小程序自动化测试


    介绍

    什么是MiniTest

    MiniTest是专属于小程序和小游戏的自动化测试框架,由三个部分组成,

    1. Minium小程序自动化框架
    2. 小程序云测试服务
    3. 小游戏云测试服务

    什么是Minium

    minium 是为小程序专门开发的自动化框架。使用 minium 可以进行小程序 UI 自动化测试, 但是 minium 的功能不止于仅仅是 UI 自动化, 甚至可以使用 minium 来进行函数的 mock, 可以直接跳转到小程序某个页面并设置页面数据, 做针对性的全面测试, 这些都得益于我们开放了部分小程序 API 的能力。除此之外,小程序有部分组件使用了系统原生的组件,对于这部分的组件,我们也基于 uiautomator 和 wda 做了补充。

    特性

    支持一套脚本,iOS & Android & 模拟器,三端运行
    提供丰富的页面跳转方式,看不到也能去得到
    可以获取和设置小程序页面数据,让测试不止点点点
    可以直接触发小程序元素绑定事件
    支持往 AppSerive 注入代码片段执行
    可以调用部分 wx 对象上的接口
    支持 Mock wx 对象上的接口
    支持 Hook wx 对象上的接口
    通过 suite 方式管理用例,config 管理运行设备

    缺点

    暂不支持H5页面的调试
    开放社区人数较少,网上教程稀少,遇到问题需要啃源码和文档

    安装

    运行环境

    Python 3.8及以上
    微信开发者工具 (本文档中简称IDE)最新版本,并打开安全模式: 设置 -> 安全设置 -> 服务端口: 打开
    微信 >= 7.0.7 (确认微信公共库版本 >= 2.7.3即可)

    安装

    自动安装
    pip3 install minium
    
    • 1
    手动安装
    #下载
    pip3 install https://minitest.weixin.qq.com/minium/Python/dist/minium-latest.zip
    #安装
    python3 setup.py install
    
    • 1
    • 2
    • 3
    • 4

    环境检查

    cmd运行

    minitest -v
    
    • 1

    输出版本号即说明环境安装成功

    功能验证

    import minium
    mini = minium.Minium({
        "project_path": "E:\code\front\dist",
        "dev_tool_path": "D:\微信web开发者工具\cli.bat",
    })
    print(mini.get_system_info())
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    project_path 【小程序项目目录地址】
    dev_tool_path 【开发者工具cli地址】,macOS: <安装路径>/Contents/MacOS/cli, Windows: <安装路径>/cli.bat

    初试框架

    配置文件

    根目录下新建config.json

    {
      "project_path": "E:\code\front\dist",
      "dev_tool_path": "D:\微信web开发者工具\cli.bat",
      "debug_mode": "info", #日记级别 debug error
      "auto_authorize": true, #自动处理授权弹窗
      "test_port": 9420, #IDE端口
      "platform": "ide", #运行平台ide, Android, IOS
      "device_desire": {
        "serial": "b777a722"
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    测试计划

    根目录新建suite.json

    {
      "pkg_list": [
        {
          "case_list": [
            "test_*"
          ],
          "pkg": "test_case.test_*"
        }
      ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    pkg_list 执行用例的内容和顺序,为一个数组,可配置多个值
    case_list case执行规则,为一个数组,可配置多个值
    pkg 测试文件执行规则

    第一个测试用例

    import minium
    class FirstTest(minium.MiniTest):
        def test_get_system_info(self):
            sys_info = self.mini.get_system_info()
            self.assertIn("SDKVersion", sys_info)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    执行测试用例

    minitest -s suite.json -c config.json -g
    
    • 1

    -s 测试计划
    -c 配置文件
    -g 生成网页版测试报告

    结果与报告

    测试结果

    每条用例的测试结果我们会存放到一个目录里面,里面包含:
    1.包含用例执行信息的json文件
    2.用例运行中的截图
    3.用例运行中的日志
    4.小程序运行中的日志
    基于这些数据可以生成测试报告,也可以做一些存档的事情。

    测试报告

    根据用例的执行结果,我们基于Vue和element提供一个简洁的测试报告:
    报告生成有2种方式:
    1.执行用例的时候加上-g参数针
    2.对已经生成的用例结果目录
    minireport input_path output_path

    input_path 测试结果目录
    output_path 测试报告存储目录

    框架进阶

    脚手架

    import minium
    
    class FirstTest(minium.MiniTest):
    
        @classmethod
        def setUpClass(cls):
            print('****所有用例执行前****')
    
        @classmethod
        def tearDownClass(cls):
            print('****所有用例执行后****')
    
        def setUp(self):
            print('****每个用例执行前****')
    
        def tearDown(self):
            print('****每个用例执行后****')
    
        def test_get_system_info1(self):
            print("in the test_get_system_info1")
    
        def test_get_system_info2(self):
            print("in the test_get_system_info2")
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    数据驱动

    import minium
    data = [(1,2),(3,4)]
    @minium.ddt_class
    class FirstTest(minium.MiniTest):
    
        @minium.ddt_case(*data)
        @minium.ddt_unpack
        def test_get_system_info(self,num1,num2):
            print(num1,'*****',num2)
            print(num1+num2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    截图

    screen_shot()

    import shutil
    import minium
    import os
    
    class FirstTest(minium.MiniTest):
    
        def test_get_system_info(self):
            output_path = os.path.join(os.path.dirname(__file__), "outputs\\test_get_system_info.png")
            if not os.path.isdir(os.path.dirname(output_path)):
                os.mkdir(os.path.dirname(output_path))
            if os.path.isfile(output_path):
                os.remove(output_path)
            self.app.screen_shot(output_path)
            self.assertTrue(os.path.isfile(output_path))
            if os.path.isdir(os.path.dirname(output_path)):
                shutil.rmtree(os.path.dirname(output_path))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    页面跳转

    go_home() 跳转到小程序首页
    navigate_to() 以导航的方式跳转到指定页面
    redirect_to() 关闭当前页面,重定向到应用内的某个页面
    switch_tab() 跳转到 tabBar 页面

    import minium
    import time
    
    class FirstTest(minium.MiniTest):
    
        @classmethod
        def setUpClass(cls):
            print('****setUpClass开始暂停等待获取token****')
            time.sleep(5)
    
    
        @classmethod
        def tearDownClass(cls):
            print('****关闭IDE****')
            cls.mini.shutdown()
    
        def test_get_system_info(self):
            time.sleep(2)
            self.app.redirect_to("/pages/ClassList/ClassList")
            time.sleep(2)
            self.app.go_home()
            time.sleep(2)
            self.app.navigate_to("/pages/AssignTaskList/AssignTaskList")
            time.sleep(2)
            self.app.go_home()
            time.sleep(2)
            self.app.switch_tab("/pages/UserCenter/UserCenter")
            time.sleep(2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    元素定位

    在这里插入图片描述

    import minium
    import time
    
    class FirstTest(minium.MiniTest):
    
        @classmethod
        def setUpClass(cls):
            print('****setUpClass开始暂停等待获取token****')
            time.sleep(5)
            
        @classmethod
        def tearDownClass(cls):
            print('****关闭IDE****')
            cls.mini.shutdown()
    
        def test_get_system_info(self):
            time.sleep(2)
            self.app.switch_tab('/pages/TaskRecord/TaskRecord')
            self.page.get_element("/page/view/view/view/view[3]/view/view[4]",max_timeout=5).tap()
            time.sleep(2)
            self.page.get_element("/page/view/view/view/view[4]/view/view[1]/view[1]/view[2]/view[3]/view", max_timeout=5).tap()
            time.sleep(2)
            self.page.get_element("/page/view/view/view/view[4]/view/view[2]/view[2]/view[2]/form/button",max_timeout=5).tap()
            time.sleep(2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    get_element() 在当前页面查询控件, 如果匹配到多个结果, 则返回第一个匹配到的结果
    get_elements() 在当前页面查询控件, 并返回一个或者多个结果

    真机调试

    Android

    配置测试 config.json

    {
      "project_path": "E:\code\front\dist",
      "dev_tool_path": "D:\微信web开发者工具\cli.bat",
      "debug_mode": "info",
      "auto_authorize": true,
      "platform": "Android",
      "test_port": 9420,
      "device_desire": {
        "serial": "b777a722"
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    adb devices命令获取serial
    在这里插入图片描述
    获取微信测试APK安装命令

    minitest --apk
    
    • 1

    在这里插入图片描述
    安装微信测试APK

    adb install -r D:\soft\Python\Python39\lib\site-packages\minium\native\lib\at\bin\AtServer.apk
    
    • 1

    在这里插入图片描述
    执行测试用例

    minitest -s suite.json -c config.json -g
    
    • 1
    IOS

    安装 libmobiledevice

    brew uninstall ideviceinstaller
    brew uninstall libimobiledevice
    brew install --HEAD libimobiledevice
    brew link --overwrite libimobiledevice
    brew install ideviceinstaller
    brew link --overwrite ideviceinstaller
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    如果没有安装过直接 brew install ideviceinstaller 即可。
    当然你也可以本地编译:

    git clone https://github.com/libimobiledevice/libimobiledevice.git
    cd libimobiledevice
    ./autogen.sh --disable-openssl
    make
    sudo make install
    
    • 1
    • 2
    • 3
    • 4
    • 5

    配置 WebDriverAgent
    minium 不包含 WebDriverAgent(简称wda) 工程,先执行以下命令clone工程:

    mkdir wda
    cd wda
    echo "{}" > package.json
    npm i appium
    echo `pwd`/node_modules/appium/node_modules/appium-webdriveragent
    
    • 1
    • 2
    • 3
    • 4
    • 5

    以上最后输出的路径为wda工程路径,可用xcode打开,也可写到device_desire配置中
    按照以下指引配置工程
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    配置完成之后,可以用⌘+u快捷键运行 unit test 测试 wda 是否正常运行
    在这里插入图片描述
    配置测试 config.json

    {
      "platform": "iOS",
      "device_desire":{
        "wda_project_path": "/Users/sherlock/wda/node_modules/appium/node_modules/appium-webdriveragent", //自定义 wda 的路径
        "device_info": {
              "udid": "aee531018e668ff1aadee0889f5ebe21a2292...", //手机的 udid 
              "model": "iPhone XR",
              "version": "12.2.5",
              "name": "sherlock's iPhone"
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    执行测试用例

    minitest -s suite.json -c config.json -g
    
    • 1

    其他

    mock相关

    相关函数

    mock_wx_method()  #mock掉小程序API的调用
    restore_wx_method()  #去掉函数的mock
    mock_request()  #mock wx.request 方法,根据正则匹配结果返回特定构造的数据
    mock_request_once()  #mock wx.request 方法,根据正则匹配结果返回特定构造的数据,无需调用restore_request()
    restore_request()  #清除掉所有mock request的匹配规则
    
    • 1
    • 2
    • 3
    • 4
    • 5

    hook相关

    相关函数

    hook_wx_method()  #hook小程序API的调用
    release_hook_wx_method()  #释放hook小程序API的调用。
    hook_current_page_method()  #hook当前页面上的方法。
    release_hook_current_page_method()  #释放当前页面方法的监听函数。
    
    • 1
    • 2
    • 3
    • 4

    性能相关

    相关函数

    start_get_perf()  #获取 CPU 内存 数据
    stop_get_perf()  #停止获取 CPU 内存 数据
    
    • 1
    • 2
  • 相关阅读:
    分布式系统的一致性与共识(1)-综述
    [附源码]Python计算机毕业设计GuiTar网站设计
    做点华为云代理业务,挣点小收入
    中秋学习Qt6
    Vue项目实战——【基于 Vue3.x + Vant UI】实现一个多功能记账本(项目演示、涉及知识点、源码分享)
    redis的事务
    流媒体分析之rtmp 协议flv 封装
    Linux 操作系统原理 — Traffic Control 流量控制与 IP QoS 技术解析
    一文解决 Go 安装和常用环境变量的配置
    HTML 表格及练习
  • 原文地址:https://blog.csdn.net/weixin_44190275/article/details/127995984