微信小程序自動化框架 minium 實踐

圖片

作者:TianTian

部門:業務技術 / 測試

一、背景需求

精選小程序發生了一次線上問題,測試階段的小程序開發碼測試 ok,但是小程序正式碼由於打包問題,"我的訂單" 頁面文件打包失敗,導致線上用戶訪問我的頁面白屏。

當前並不能避免該打包問題,爲了規避異常版本發佈至線上,需要在預發、體驗碼發佈、正式碼發佈等各階段進行主流程迴歸。手動迴歸測試非常耗時,在發佈前的各階段,測試人員須重複執行大量測試用例,以確保本次上線功能 OK 且對其他功能無影響。

一遍又一遍執行相同的測試用例,不僅要花費更多的時間,而且還會降低整體測試效率,因此引入微信小程序自動化以解放重複人力。

二、調研

1.Jest + 小程序 SDK

優點:

缺點:

2.minium 框架

優點:

缺點:

3. 選型

精選小程序主要是原生頁面,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: mFNZLR

代碼示例:

#!/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)

四、環境搭建

安裝命令:pip3 install minium-latest.zip 或者python3 setup.py install
minitest -v
"path/to/cli" auto --project "path/to/project" --auto-port 9420

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

macOS: <安裝路徑>/Contents/MacOS/cli
Windows: <安裝路徑>/cli.bat

五、小程序腳本編寫

思路:使用 Page Object 架構,使系統架構分層,每一個頁面設計爲一個 Class,包含了頁面需要測試的元素,測試用例只要關心測試的數據即可;

1. 目錄結構

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

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()

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()

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

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)

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
}

6.minitest 命令行

minium 安裝時執行的 setup.py 文件,指定了 minitest 命令運行的方法入口爲:minium.framework.loader:main  loader.py 文件解釋了運行的命令行的含義 

7.suite 測試計劃文件

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

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
  1. 打開微信開發者工具超時  微信開發者工具:設置 - 代理設置,關閉 ide 的代理  3. 連接開發者工具後報錯 
原因:可能是微信開發者工具和minium的版本不一致;
我測試使用ok的匹配版本爲:
Minium版本:1.0.5
開發者工具版本:1.05.2102010
python版本:3.8.8
  1. 出現以下報錯,可能是登陸的開發者工具的賬號,沒有被測試小程序的開發者權限;  5. 運行過程中,發現調用截圖的方法比較耗時,但是在 config 文件設置了 "assert_capture": false, 配置沒生效,仍然會去調用截圖的方法;  ps:猜測是一個 bug,然後給微信社區留言了,最新版本 1.0.6 修復了這個問題 原因:是框架的 minitest.py 文件調用 setup 和 TearDown 方法的時候,沒有判斷配置文件 "assert_capture": false 這個條件  可以修改 minitest.py 文件,增加配置文件的判斷條件,修改如下:
if self.test_config.assert_capture:

            self.capture("setup")
  1. 命令行執行的時候加了 - p xxx 參數,運行時報引入的包不存在  原因:命令行運行時默認是當前路徑, 加 - p xxx, 這樣會導致腳本運行的 PYTHONPATH 變了(不是當前目錄了),這樣會導致包不存在  解決方法:

七、參考資料

  1. 微信官方文檔

  2. 簡書上 Rethink 的相關文章介紹

    招募優秀的你加入👇

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Q4z4JQTkhmumE4Qwp3cgPQ