瀏覽器領域,我們有如selenium和puppeteer這樣的庫,可以自動化控制瀏覽器執行自動化腳本,以完成自動化端對端測試、定時自動化任務等。隨著持續集成、持續部署也就是CI/CD的需求日益增長,自動化也成為必不可少的一環。
對于日益增長的小程序開發需求,我們能不能自動化控制小程序呢,進而達成自動測試、自動發布等任務呢?
針對微信小程序,自2019年5月,微信官方也開始提供了一個官方的自動化SDK: miniprogram-automator 。這是一個通過NodeJS操控開發者工具以及遠程真機中微信的SDK。通過這個SDK,可以控制小程序跳轉到指定頁面、獲取小程序頁面數據、獲取小程序頁面元素狀態、觸發小程序元素綁定事件、往 AppService 注入代碼片段、調用 wx 對象上任意接口等等。
這個SDK通過腳本控制本機的微信開發者工具來近似達到自動化測試業務的目的,同時,也可通過遠程控制真機,達到真機測試的目的。
我們首先來體驗一下這個SDK。
首先,你需要確保你安裝了微信開發者工具,并且版本大于1.02.1907232,并設置你的基礎庫版本在2.7.3以上,同時請安裝NodeJS 8.0以上的版本。
我們知道,微信開發者工具提供了命令行與 HTTP 服務兩種接口供外部調用,開發者可以通過命令行或 HTTP 請求指示工具進行登錄、預覽、上傳等操作。
SDK通過命令行方式將微信開發者工具調起,再通過外部方式導入目標項目。微信開發者工具通過讀取目標項目的project.config.js,初始化項目。并讀取啟動命令的 --auto-port 參數,使得SDK可以通過此端口的Websocket服務,實現與對應的目標小程序調試窗口進行交互。
這就是 miniprogram-automator 工作的大致原理。
感興趣的讀者可以嘗試使用微信開發者工具的cli方式來嘗試運行此命令。
cli auto --project { 項目路徑 } --auto-port { websocket的端口 }
為了運行上述命令,我們需要找到微信開發者工具的安裝目錄。不同的操作系統位置不同。Mac位于:<安裝路徑>/Contents/MacOS/cli,windows位于: <安裝路徑>/cli.bat。對于經常使用的讀者,建議將cli所在的目錄放在系統的環境變量中。
你可能遇到IDE服務超時的情況。因此,為了保證開發者工具能夠通過命令行打開,需要將開發者工具的HTTP服務調用接口打開。
打開的方式是,進入微信開發者工具,選擇:設置 > 安全設置。在服務端口中選擇:打開。此時,微信開發者工具會自動指定一個可用的端口號。
細心的讀者會發現這里又出現了一個“端口”。不同于上面提到的端口,這個端口是IDE提供對外服務的端口。如上圖所示,36146是IDE服務的端口。你在啟動IDE之后,可以訪問 http://127.0.0.1:36146/open
你的IDE就會聚焦到你的面前。 http://127.0.0.1:36146/ 是IDE服務的根域名,open是命令,讀者可以參考 命令索引 通過不同的URL發出不同的命令。
好,安裝好了SDK,我們來操練一下:
首先,我們init一個npm庫如 auto ,通過: npm i miniprogram-automator --save-dev 或 yarn add miniprogram-automator --dev 即可安裝 miniprogram-automator 。
接下來,我們下載一個 微信示例程序 ,并解壓在~/demo-miniapp/
下面,我們新建一個文件,如index.js,內容如下:
const automator = require('miniprogram-automator') automator.launch({ projectPath: '~/demo-miniapp/', // 項目文件地址 }).then(async miniProgram => { const page = await miniProgram.reLaunch('/page/tabBar/component/index') await page.waitFor(500) const element = await page.$('.kind-list-text') console.log(await element.attribute('class')) await element.tap() await page.waitFor(500) await miniProgram.close() })
現在,讓我們運行起來。 node index.js 。我們看到,IDE自動啟動,并加載了我們的項目文件,并自動地點開了第一個項目,過一段時間之后,程序自動的退出。
這就是我們對于自動運行的初步體驗。
截至目前(2020年4月初)最新的SDK的API主要分四個模塊:Automater、MiniProgram、Page和Element等。
Automator 模塊提供了啟動及連接開發者工具的方法。開發者可以對連接地址、端口號、項目路徑等作出設置。歸根結底是對于cli的包裝。詳見: Automator
MiniProgram提供對小程序的控制。提供以下幾類支持,詳見: MiniProgram :
Page 模塊提供了控制小程序頁面的方法。提供以下幾類支持,詳見: Page :
Element 模塊提供了控制小程序頁面元素的方法。提供以下幾類支持,詳見: Element :
可以看出,除Automator之外,每個API模塊都在自己的領域內提供對小程序自身內容的訪問特性以及擴充特性。這比較類似于Pupputeer的API設定。
miniprogram-automator 本身不提供測試框架,我們可以選用熟悉的測試框架與之整合。這里我們以jest為例。其他諸如mocha、jasmine、Cucumber都比較類似。
現在,在我們之前的項目 auto 里安裝jest。 npm i jest -g 或 yarn global add jest
簡單科普下jest的工作原理。在項目中,jest識別三種測試文件:
Jest 在進行測試的時候,它會在整個項目進行查找,只要碰到這三種文件它都會執行。Jest有以下設定:
Jest 測試提供了一些測試的生命周期 API。可以輔助我們在每個 case 的開始和結束做一些處理。 這樣,在進行一些和數據相關的測試時,可以在測試前準備一些數據,在測試后,清理測試數據。 4 個主要的生命周期函數:
回到 miniprogram-automator 。我們可以在 auto 項目中加入一個index.spec.js文件。
const automator = require('miniprogram-automator') let miniProgram, page beforeAll(async () => { miniProgram = await automator.launch({ projectPath: '/Users/liuguanyu/devspace/demo-miniapp/' }) page = await miniProgram.reLaunch('/page/tabBar/component/index') await page.waitFor(500) }, 50000) afterAll(async () => { await miniProgram.close() })
現在,你在 auto 下運行 jest 會報錯。
意思是需要我們增加至少一個測試套件或測試用例。
為此我們增加一個測試套件,并增加一些測試用例,修改如下:
const automator = require('miniprogram-automator') let miniProgram, page beforeAll(async () => { miniProgram = await automator.launch({ projectPath: '/Users/liuguanyu/devspace/demo-miniapp/' }) page = await miniProgram.reLaunch('/page/tabBar/component/index') await page.waitFor(500) }, 50000) describe("測試微信小程序", () => { // 1. 測試頂部描述 it("標題欄", async () => { const desc = await page.$('.index-desc') // 要求測試標簽名必須為view expect(desc.tagName).toBe('view') // 要求測試內容包含文字以下將展示小程序官方組件能力 expect(await desc.text()).toContain('以下將展示小程序官方組件能力') }) // 2. 測試列表項 it('列表項', async () => { const lists = await page.$$('.kind-list-item') // 測試共有7個列表項 expect(lists.length).toBe(7) const list = await lists[0].$('.kind-list-item-hd') //第一個列表元素的標題應該是“視圖窗器” expect(await list.text()).toBe('視圖容器') }) // 3. 測試列表項行為 it('列表行為', async () => { const listHead = await page.$('.kind-list-item-hd') // 點擊應展開未展開項 expect(await listHead.attribute('class')).toBe('kind-list-item-hd') await listHead.tap() await page.waitFor(200) expect(await listHead.attribute('class')).toBe( 'kind-list-item-hd kind-list-item-hd-show', ) // 再次點擊應合上 await listHead.tap() await page.waitFor(200) expect(await listHead.attribute('class')).toBe('kind-list-item-hd') // 點擊子列表項應該會跳轉到指定頁面 await listHead.tap() await page.waitFor(200) const item = await page.$('.index-bd navigator') await item.tap() await page.waitFor(1500) expect((await miniProgram.currentPage()).path).toBe('page/component/pages/view/view') }) // 4. 驗證wxml方法和setData方法及快照比對 it('驗證WXML', async () => { const element = await page.$('page') expect(await element.wxml()).toMatchSnapshot() await page.setData({ list: [] }) expect(await element.wxml()).toMatchSnapshot() }) // 5. mock方法測試并還原 it('偽造請求結果', async () => { // 偽造請求數據 const mockData = [{ rule: 'testRequest', result: { data: 'test', cookies: [], header: {}, statusCode: 200, } }] // mock方法 await miniProgram.mockWxMethod( 'request', function (obj, data) { for (let i = 0, len = data.length; i < len; i++) { const item = data[i] const rule = new RegExp(item.rule) if (rule.test(obj.url)) { return item.result } // 沒命中規則的真實訪問后臺 return new Promise(resolve => { obj.success = res => resolve(res) obj.fail = res => resolve(res) this.origin(obj) }) } }, mockData ) // 請求mock的方法 const result = await miniProgram.callWxMethod('request', { url: 'https://14592619.qcloud.la/testRequest', }) expect(result.data).toBe('test') // 還原方法 await miniProgram.restoreWxMethod('request') }, 30000) }) afterAll(async () => { await miniProgram.close() })
此時,我們在 auto 目錄下再次運行 jest ,則得到如下結果:
我們看到所有的測試都已通過。
真機測試可以自動測試以及掃碼測試。此時可以在beforeAll里面加入 await miniProgram.remote(true) 。這個true如果不寫,就需要用真機掃碼測試。當編譯好之后,開發者工具會自動將小程序和調試工具發送到真機。并在側邊增加了測試條。
當不寫true時候,運行到remote時,會彈出這樣的對話框:
發布3年多,微信小程序已經從微信生態的一環,逐步向多領域滲透。隨著開發者的日益增多,面向開發的工具也逐步完善。本文試圖管中窺豹,給大家介紹了微信自動化的主要環節。時至今日,小程序已經成長為一種重要的產品形式和生態環境。越來越多的小程序平臺正在自己的領域以不同的形式給小程序這個產品形式添磚加瓦。小程序生態和開發環節的完善和成長,也需要廣大平臺、開發者共同努力,將這一Created in China 的生態體系發揚光大。