自动化测试

    引自 :

    有几种方法可以使用 WebDriver 设置测试。

    (WDIO) 是一个自动化测试框架,它提供了一个 Node.js 软件包用于测试 Web 驱动程序。 它的生态系统还包括各种插件(例如报告器和服务) ,可以帮助你把测试设置放在一起。

    Install the test runner

    首先,你需要在项目根目录中运行 WebdriverIO 启动工具包:

    • npm
    • Yarn

    这将安装所有必要的软件包,并生成一个 wdio.conf.js 配置文件。

    将 WDIO 连接到 Electron 应用程序

    更新配置文件中的选项以指向 Electron 应用二进制文件:

    wdio.conf.js

    1. export.config = {
    2. // ...
    3. capabilities: [{
    4. browserName: 'chrome',
    5. 'goog:chromeOptions': {
    6. binary: '/path/to/your/electron/binary', // Electron 二进制文件的路径
    7. args: [/* 命令行参数 */] // 可选, 比如 'app=' + /path/to/your/app/
    8. }
    9. }]
    10. // ...
    11. }

    运行测试

    执行命令:

    1. $ npx wdio run wdio.conf.js

    Selenium 是一个Web自动化框架,以多种语言公开与 WebDriver API 的绑定方式。 Node.js 环境下, 可以通过 NPM 安装 selenium-webdriver 包来使用此框架。

    运行 ChromeDriver 服务

    为了与 Electron 一起使用 Selenium ,你需要下载 electron-chromedriver 二进制文件并运行它:

    • npm
    • Yarn
    1. npm install --save-dev electron-chromedriver
    2. ./node_modules/.bin/chromedriver
    3. Starting ChromeDriver (v2.10.291558) on port 9515
    4. Only local connections are allowed.
    1. yarn add --dev electron-chromedriver
    2. ./node_modules/.bin/chromedriver
    3. Starting ChromeDriver (v2.10.291558) on port 9515
    4. Only local connections are allowed.

    记住 9515 这个端口号,我们后面会用到.

    将 Selenium 连接到 ChromeDriver

    接下来,把 Selenium 安装到你的项目中:

    • npm
    • Yarn
    1. npm install --save-dev selenium-webdriver

    在 Electron 下使用 selenium-webdriver 和其平时的用法并没有大的差异,只是你需要手动设置如何连接 ChromeDriver,以及 Electron 应用的查找路径:

    test.js

    1. const webdriver = require('selenium-webdriver')
    2. const driver = new webdriver.Builder()
    3. // 端口号 "9515" 是被 ChromeDriver 开启的.
    4. .usingServer('http://localhost:9515')
    5. .withCapabilities({
    6. 'goog:chromeOptions': {
    7. // 这里填您的Electron二进制文件路径。
    8. binary: '/Path-to-Your-App.app/Contents/MacOS/Electron'
    9. }
    10. })
    11. .forBrowser('chrome') // 注意: 使用 .forBrowser('electron') for selenium-webdriver <= 3.6.0
    12. .build()
    13. driver.get('http://www.google.com')
    14. driver.findElement(webdriver.By.name('q')).sendKeys('webdriver')
    15. driver.findElement(webdriver.By.name('btnG')).click()
    16. driver.wait(() => {
    17. return driver.getTitle().then((title) => {
    18. return title === 'webdriver - Google Search'
    19. })
    20. }, 1000)
    21. driver.quit()

    您可以通过 Node.js 包管理器安装 Playwright。 Playwright团队推荐使用 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD 环境变量来避免在测试 Electron 软件时进行不必要的浏览器下载。

    • npm
    • Yarn
    1. PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm install --save-dev playwright

      Playwright 同时也有自己的测试运行器( Playwright Test ),可用作端到端(E2E)测试。 你也可以在项目中作为开发以来来安装它:

      • npm
      • Yarn
      1. npm install --save-dev @playwright/test
      1. yarn add --dev @playwright/test

      ::: 依赖注意事项

      本教程的编写基于 playwright@1.16.3@playwright/test@1.16.3 查看 页面以知晓可能会影响到的代码更改。

      :::

      :::使用第三方测试运行程序的信息

      如果您有兴趣使用其他测试运行其(例如 Jest 或 Mocha),请查看 Playwright 的 第三方测试运行器 指南。

      :::

      Playwright通过 _electron.launch API在开发模式下启动您的应用程序。 要将此 API 指向 Electron 应用,可以将路径传递到主进程入口点(此处为 main.js)。

      1. const { _electron: electron } = require('playwright')
      2. const { test } = require('@playwright/test')
      3. test('launch app', async () => {
      4. const electronApp = await electron.launch({ args: ['main.js'] })
      5. // close app
      6. })

      在此之后,您将可以访问到 Playwright 的 ElectronApp 类的一个实例。 这是一个功能强大的类,可以访问主进程模块,例如:

      它还可以从 Electron BrowserWindow 实例创建单独的 Page 对象。 例如,获取第一个 BrowserWindow 并保存一个屏幕截图:

      1. const { _electron: electron } = require('playwright')
      2. const { test } = require('@playwright/test')
      3. test('save screenshot', async () => {
      4. const electronApp = await electron.launch({ args: ['main.js'] })
      5. const window = await electronApp.firstWindow()
      6. await window.screenshot({ path: 'intro.png' })
      7. // 关闭应用程序
      8. await electronApp.close()
      9. })

      使用 PlayWright 测试运行器将所有这些组合到一起,让我们创建一个有单个测试和断言的 example.spec.js 测试文件:

      example.spec.js

      1. const { _electron: electron } = require('playwright')
      2. const { test, expect } = require('@playwright/test')
      3. test('example test', async () => {
      4. const electronApp = await electron.launch({ args: ['.'] })
      5. const isPackaged = await electronApp.evaluate(async ({ app }) => {
      6. // 在 Electron 的主进程运行,这里的参数总是
      7. // 主程序代码中 require('electron') 的返回结果。
      8. return app.isPackaged;
      9. });
      10. expect(isPackaged).toBe(false);
      11. // 等待第一个 BrowserWindow 打开
      12. // 然后返回它的 Page 对象
      13. const window = await electronApp.firstWindow()
      14. await window.screenshot({ path: 'intro.png' })
      15. // 关闭应用程序
      16. await electronApp.close()
      17. });

      然后,使用 npx playwright test 运行 Playwright 测试。 您应该在您的控制台中看到测试通过,并在您的文件系统上看到一个屏幕截图 intro.png

      1. $ npx playwright test
      2. Running 1 test using 1 worker
      3. example.spec.js:4:1 example test (1s)

      info

      :::延伸阅读

      查看 Playwright完整的 和 ElectronApplication class API。

      :::

      当然,也可以使用node的内建IPC STDIO来编写自己的自定义驱动。 自定义测试驱动程序需要您写额外的应用代码,但是有较低的开销,让您 在您的测试套装上显示自定义方法。

      我们将用 Node.js 的 child_process API 来创建一个自定义驱动。 测试套件将生成 Electron 子进程,然后建立一个简单的消息传递协议。

      testDriver.js

      1. const childProcess = require('child_process')
      2. const electronPath = require('electron')
      3. // 启动子进程
      4. const env = { /* ... */ }
      5. const stdio = ['inherit', 'inherit', 'inherit', 'ipc']
      6. const appProcess = childProcess.spawn(electronPath, ['./app'], { stdio, env })
      7. // 侦听应用传来的IPC信息
      8. appProcess.on('message', (msg) => {
      9. // ...
      10. })
      11. // 向应用发送IPC消息
      12. appProcess.send({ my: 'message' })

      在 Electron 应用程序中,您可以使用 Node.js 的 API 监听消息并发送回复:

      main.js

      1. // 监听测试套件发送过来的消息
      2. // ...
      3. })
      4. process.send({ my: 'message' })

      现在,我们可以使用appProcess 对象从测试套件到Electron应用进行通讯。

      为方便起见,您可能希望将 appProcess 包装在一个提供更高级功能的驱动程序对象中。 下面是一个示例。 让我们从创建一个 TestDriver 类开始:

      testDriver.js

      1. class TestDriver {
      2. constructor ({ path, args, env }) {
      3. this.rpcCalls = []
      4. // 启动子进程
      5. env.APP_TEST_DRIVER = 1 // 让应用知道它应当侦听信息
      6. this.process = childProcess.spawn(path, args, { stdio: ['inherit', 'inherit', 'inherit', 'ipc'], env })
      7. // 处理RPC回复
      8. this.process.on('message', (message) => {
      9. // 弹出处理器
      10. const rpcCall = this.rpcCalls[message.msgId]
      11. if (!rpcCall) return
      12. this.rpcCalls[message.msgId] = null
      13. // 拒绝/接受(reject/resolve)
      14. if (message.reject) rpcCall.reject(message.reject)
      15. else rpcCall.resolve(message.resolve)
      16. })
      17. // 等待准备完毕
      18. this.isReady = this.rpc('isReady').catch((err) => {
      19. console.error('Application failed to start', err)
      20. this.stop()
      21. process.exit(1)
      22. })
      23. }
      24. // 简单 RPC 回调
      25. // 可以使用:driver.rpc('method', 1, 2, 3).then(...)
      26. async rpc (cmd, ...args) {
      27. // 发送 RPC 请求
      28. const msgId = this.rpcCalls.length
      29. this.process.send({ msgId, cmd, args })
      30. return new Promise((resolve, reject) => this.rpcCalls.push({ resolve, reject }))
      31. }
      32. stop () {
      33. this.process.kill()
      34. }
      35. }
      36. module.exports = { TestDriver };

      然后,在您的应用代码中,可以编写一个简单的处理程序来接收 RPC 调用:

      main.js

      然后,在您的测试套件中,您可以使用TestDriver 类用你选择的自动化测试框架。 下面的示例使用 ava,但其他流行的选择,如Jest 或者Mocha 也可以:

      test.js

      1. const test = require('ava')
      2. const electronPath = require('electron')
      3. const { TestDriver } = require('./testDriver')
      4. const app = new TestDriver({
      5. path: electronPath,
      6. args: ['./app'],
      7. env: {
      8. NODE_ENV: 'test'
      9. }
      10. })
      11. test.before(async t => {
      12. await app.isReady
      13. })
      14. test.after.always('cleanup', async t => {
      15. })