測試自動化是一種驗證您的應(yīng)用是否如你預(yù)期那樣正常工作的有效方法。 Electron已然不再維護自己的測試解決方案, 本指南將會介紹幾種方法,讓您可以在 Electron 應(yīng)用上進行端到端自動測試。
引自 ChromeDriver - WebDriver for Chrome:
WebDriver 是一款開源的支持多瀏覽器的自動化測試工具。 它提供了操作網(wǎng)頁、用戶輸入、JavaScript 執(zhí)行等能力。 ChromeDriver 是一個實現(xiàn)了 WebDriver 與 Chromium 聯(lián)接協(xié)議的獨立服務(wù)。 它也是由開發(fā)了 Chromium 和 WebDriver 的團隊開發(fā)的。
有幾種方法可以使用 WebDriver 設(shè)置測試。
WebdriverIO (WDIO) 是一個自動化測試框架,它提供了一個 Node.js 軟件包用于測試 Web 驅(qū)動程序。 它的生態(tài)系統(tǒng)還包括各種插件(例如報告器和服務(wù)) ,可以幫助你把測試設(shè)置放在一起。
安裝測試運行器
首先,你需要在項目根目錄中運行 WebdriverIO 啟動工具包:
npm | Yarn |
|
|
這將安裝所有必要的軟件包,并生成一個 wdio.conf.js
配置文件。
更新配置文件中的選項以指向 Electron 應(yīng)用二進制文件:
export.config = {
// ...
capabilities: [{
browserName: 'chrome',
'goog:chromeOptions': {
binary: '/path/to/your/electron/binary', // Electron 二進制文件的路徑
args: [/* 命令行參數(shù) */] // 可選, 比如 'app=' + /path/to/your/app/
}
}]
// ...
}
執(zhí)行命令:
$ npx wdio run wdio.conf.js
Selenium 是一個Web自動化框架,以多種語言公開與 WebDriver API 的綁定方式。 Node.js 環(huán)境下, 可以通過 NPM 安裝 selenium-webdriver
包來使用此框架。
為了與 Electron 一起使用 Selenium ,你需要下載 electron-chromedriver
二進制文件并運行它:
npm | Yarn |
|
|
記住 9515
這個端口號,我們后面會用到.
接下來,把 Selenium 安裝到你的項目中:
npm | Yarn |
|
|
在 Electron 下使用 selenium-webdriver
和其平時的用法并沒有大的差異,只是你需要手動設(shè)置如何連接 ChromeDriver,以及 Electron 應(yīng)用的查找路徑:
const webdriver = require('selenium-webdriver')
const driver = new webdriver.Builder()
// 端口號 "9515" 是被 ChromeDriver 開啟的.
.usingServer('http://localhost:9515')
.withCapabilities({
'goog:chromeOptions': {
// 這里填您的Electron二進制文件路徑。
binary: '/Path-to-Your-App.app/Contents/MacOS/Electron'
}
})
.forBrowser('chrome') // 注意: 使用 .forBrowser('electron') for selenium-webdriver <= 3.6.0
.build()
driver.get('http://www.google.com')
driver.findElement(webdriver.By.name('q')).sendKeys('webdriver')
driver.findElement(webdriver.By.name('btnG')).click()
driver.wait(() => {
return driver.getTitle().then((title) => {
return title === 'webdriver - Google Search'
})
}, 1000)
driver.quit()
Microsoft Playwright 是一個端到端的測試框架,使用瀏覽器特定的遠程調(diào)試協(xié)議架構(gòu),類似于 Puppeteer 的無頭 Node.js API,但面向端到端測試。 Playwright 通過 Electron 支持 [Chrome DevTools 協(xié)議][] (CDP) 獲得實驗性的 Electron 支持。
您可以通過 Node.js 包管理器安裝 Playwright。 Playwright團隊推薦使用 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD
環(huán)境變量來避免在測試 Electron 軟件時進行不必要的瀏覽器下載。
npm | Yarn |
|
|
Playwright 同時也有自己的測試運行器( Playwright Test ),可用作端到端(E2E)測試。 你也可以在項目中作為開發(fā)以來來安裝它:
npm | Yarn |
|
|
::: 依賴注意事項
本教程的編寫基于 playwright@1.16.3
和 @playwright/test@1.16.3
查看Playwright 的版本更新 頁面以知曉可能會影響到的代碼更改。
:::
:::使用第三方測試運行程序的信息
如果您有興趣使用其他測試運行其(例如 Jest 或 Mocha),請查看 Playwright 的 第三方測試運行器 指南。
:::
Playwright通過 _electron.launch
API在開發(fā)模式下啟動您的應(yīng)用程序。 要將此 API 指向 Electron 應(yīng)用,可以將路徑傳遞到主進程入口點(此處為 main.js
)。
const { _electron: electron } = require('playwright')
const { test } = require('@playwright/test')
test('launch app', async () => {
const electronApp = await electron.launch({ args: ['main.js'] })
// close app
await electronApp.close()
})
在此之后,您將可以訪問到 Playwright 的 ElectronApp
類的一個實例。 這是一個功能強大的類,可以訪問主進程模塊,例如:
const { _electron: electron } = require('playwright')
const { test } = require('@playwright/test')
test('get isPackaged', async () => {
const electronApp = await electron.launch({ args: ['main.js'] })
const isPackaged = await electronApp.evaluate(async ({ app }) => {
// 在 Electron 的主進程運行,這里的參數(shù)總是
// 主程序代碼中 require('electron') 的返回結(jié)果。
return app.isPackaged
})
console.log(isPackaged) // false(因為我們處在開發(fā)環(huán)境)
// 關(guān)閉應(yīng)用程序
await electronApp.close()
})
它還可以從 Electron BrowserWindow 實例創(chuàng)建單獨的 Page 對象。 例如,獲取第一個 BrowserWindow 并保存一個屏幕截圖:
const { _electron: electron } = require('playwright')
const { test } = require('@playwright/test')
test('save screenshot', async () => {
const electronApp = await electron.launch({ args: ['main.js'] })
const window = await electronApp.firstWindow()
await window.screenshot({ path: 'intro.png' })
// 關(guān)閉應(yīng)用程序
await electronApp.close()
})
使用 PlayWright 測試運行器將所有這些組合到一起,讓我們創(chuàng)建一個有單個測試和斷言的 example.spec.js
測試文件:
const { _electron: electron } = require('playwright')
const { test, expect } = require('@playwright/test')
test('example test', async () => {
const electronApp = await electron.launch({ args: ['.'] })
const isPackaged = await electronApp.evaluate(async ({ app }) => {
// 在 Electron 的主進程運行,這里的參數(shù)總是
// 主程序代碼中 require('electron') 的返回結(jié)果。
return app.isPackaged;
});
expect(isPackaged).toBe(false);
// 等待第一個 BrowserWindow 打開
// 然后返回它的 Page 對象
const window = await electronApp.firstWindow()
await window.screenshot({ path: 'intro.png' })
// 關(guān)閉應(yīng)用程序
await electronApp.close()
});
然后,使用 npx playwright test
運行 Playwright 測試。 您應(yīng)該在您的控制臺中看到測試通過,并在您的文件系統(tǒng)上看到一個屏幕截圖 intro.png
。
? $ npx playwright test
Running 1 test using 1 worker
? example.spec.js:4:1 ? example test (1s)
INFO
PlayWright Test 將自動運行與正則表達式
.*(test|spec)\.(js|ts|mjs)
匹配的所有文件。 您可以在 Playwright Test 配置選項 中自定義這個正則表達式。
:::延伸閱讀
查看 Playwright完整的 Electron 和 ElectronApplication class API。
:::
當(dāng)然,也可以使用node的內(nèi)建IPC STDIO來編寫自己的自定義驅(qū)動。 自定義測試驅(qū)動程序需要您寫額外的應(yīng)用代碼,但是有較低的開銷,讓您 在您的測試套裝上顯示自定義方法。
我們將用 Node.js 的 child_process
API 來創(chuàng)建一個自定義驅(qū)動。 測試套件將生成 Electron 子進程,然后建立一個簡單的消息傳遞協(xié)議。
const childProcess = require('child_process')
const electronPath = require('electron')
// 啟動子進程
const env = { /* ... */ }
const stdio = ['inherit', 'inherit', 'inherit', 'ipc']
const appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })
// 偵聽?wèi)?yīng)用傳來的IPC信息
appProcess.on('message', (msg) => {
// ...
})
// 向應(yīng)用發(fā)送IPC消息
appProcess.send({ my: 'message' })
在 Electron 應(yīng)用程序中,您可以使用 Node.js 的 process
API 監(jiān)聽消息并發(fā)送回復(fù):
// 監(jiān)聽測試套件發(fā)送過來的消息
process.on('message', (msg) => {
// ...
})
// 發(fā)送一條消息到測試套件
process.send({ my: 'message' })
現(xiàn)在,我們可以使用appProcess
對象從測試套件到Electron應(yīng)用進行通訊。
為方便起見,您可能希望將 appProcess
包裝在一個提供更高級功能的驅(qū)動程序?qū)ο笾小?下面是一個示例。 讓我們從創(chuàng)建一個 TestDriver
類開始:
class TestDriver {
constructor ({ path, args, env }) {
this.rpcCalls = []
// 啟動子進程
env.APP_TEST_DRIVER = 1 // 讓應(yīng)用知道它應(yīng)當(dāng)偵聽信息
this.process = childProcess.spawn(path, args, { stdio: ['inherit', 'inherit', 'inherit', 'ipc'], env })
// 處理RPC回復(fù)
this.process.on('message', (message) => {
// 彈出處理器
const rpcCall = this.rpcCalls[message.msgId]
if (!rpcCall) return
this.rpcCalls[message.msgId] = null
// 拒絕/接受(reject/resolve)
if (message.reject) rpcCall.reject(message.reject)
else rpcCall.resolve(message.resolve)
})
// 等待準(zhǔn)備完畢
this.isReady = this.rpc('isReady').catch((err) => {
console.error('Application failed to start', err)
this.stop()
process.exit(1)
})
}
// 簡單 RPC 回調(diào)
// 可以使用:driver.rpc('method', 1, 2, 3).then(...)
async rpc (cmd, ...args) {
// 發(fā)送 RPC 請求
const msgId = this.rpcCalls.length
this.process.send({ msgId, cmd, args })
return new Promise((resolve, reject) => this.rpcCalls.push({ resolve, reject }))
}
stop () {
this.process.kill()
}
}
module.exports = { TestDriver };
然后,在您的應(yīng)用代碼中,可以編寫一個簡單的處理程序來接收 RPC 調(diào)用:
const METHODS = {
isReady () {
// 進行任何需要的初始化
return true
}
// 在這里定義可做 RPC 調(diào)用的方法
}
const onMessage = async ({ msgId, cmd, args }) => {
let method = METHODS[cmd]
if (!method) method = () => new Error('Invalid method: ' + cmd)
try {
const resolve = await method(...args)
process.send({ msgId, resolve })
} catch (err) {
const reject = {
message: err.message,
stack: err.stack,
name: err.name
}
process.send({ msgId, reject })
}
}
if (process.env.APP_TEST_DRIVER) {
process.on('message', onMessage)
}
然后,在您的測試套件中,您可以使用TestDriver
類用你選擇的自動化測試框架。 下面的示例使用 ava
,但其他流行的選擇,如Jest 或者Mocha 也可以:
const test = require('ava')
const electronPath = require('electron')
const { TestDriver } = require('./testDriver')
const app = new TestDriver({
path: electronPath,
args: ['./app'],
env: {
NODE_ENV: 'test'
}
})
test.before(async t => {
await app.isReady
})
test.after.always('cleanup', async t => {
await app.stop()
})
[Chrome DevTools 協(xié)議]: https://chromedevtools.github.io/devtools-protocol/
更多建議: