- 使用自定义驱动程序进行自动化测试
使用自定义驱动程序进行自动化测试
为Electron应用编写自动测试, 你需要一种 "驱动" 应用程序的方法。 Spectron 是一种常用的解决方案, 它允许您通过 WebDriver 模拟用户行为。 当然,也可以使用node的内建IPC STDIO来编写自己的自定义驱动。 自定义驱动的优势在于,它往往比Spectron需要更少的开销,并允许你向测试套件公开自定义方法。
要创建自定义驱动, 我们将使用 nodejs 的 child_process API。 测试套件将生成 Electron 子进程,然后建立一个简单的消息传递协议。
var childProcess = require('child_process')var electronPath = require('electron')// 生成进程var env = { /* ... */ }var stdio = ['inherit', 'inherit', 'inherit', 'ipc']var appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })// 从应用侦听IPC消息appProcess.on('message', (msg) => {// ...})// 向应用发送IPC消息appProcess.send({ my: 'message' })
从Electron应用中,你可以侦听消息并使用 nodejs 进程 API 发送答复:
// 从测试套件进程侦听IPC消息process.on('message', (msg) => {// ...})// 向测试套件进程发送IPC消息process.send({ my: 'message' })
现在,我们可以使用appProcess 对象从测试套件到Electron应用进行通讯。
为了方便起见,你可能需要封装appProcess到一个驱动对象,以便提供更多高级函数。 下面是一个如何这样做的示例:
class TestDriver {constructor ({ path, args, env }) {this.rpcCalls = []// start child processenv.APP_TEST_DRIVER = 1 // let the app know it should listen for messagesthis.process = childProcess.spawn(path, args, { stdio: ['inherit', 'inherit', 'inherit', 'ipc'], env })// handle rpc responsesthis.process.on('message', (message) => {// pop the handlervar rpcCall = this.rpcCalls[message.msgId]if (!rpcCall) returnthis.rpcCalls[message.msgId] = null// reject/resolveif (message.reject) rpcCall.reject(message.reject)else rpcCall.resolve(message.resolve)})// wait for readythis.isReady = this.rpc('isReady').catch((err) => {console.error('Application failed to start', err)this.stop()process.exit(1)})}// simple RPC call// to use: driver.rpc('method', 1, 2, 3).then(...)async rpc (cmd, ...args) {// send rpc requestvar msgId = this.rpcCalls.lengththis.process.send({ msgId, cmd, args })return new Promise((resolve, reject) => this.rpcCalls.push({ resolve, reject }))}stop () {this.process.kill()}}
在应用中, 你需要为 RPC 回调编写一个简单的处理程序:
if (process.env.APP_TEST_DRIVER) {process.on('message', onMessage)}async function onMessage ({ msgId, cmd, args }) {var method = METHODS[cmd]if (!method) method = () => new Error('Invalid method: ' + cmd)try {var resolve = await method(...args)process.send({ msgId, resolve })} catch (err) {var reject = {message: err.message,stack: err.stack,name: err.name}process.send({ msgId, reject })}}const METHODS = {isReady () {// do any setup neededreturn true}// define your RPC-able methods here}
然后, 在测试套件中, 可以按如下方式使用测试驱动程序:
var test = require('ava')var electronPath = require('electron')var 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()})
