近期的自动化测试项目中有个关于测试内部IM通信软件的需求,在了解到各个客户端的相应技术栈实现以后,在mac中的应用使用的是electron 技术,我们也对相应的技术进行调研,此文记录一下在关于electron应用的自动化测试。

一、electron 简介

electron利用 Chromium 和 Node.js, 为使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序,可以实现代码一次编译,多端运行的效果,可以生成mac app, windows exe等,但是它编译出来的文件由于包含Chromium,所以体积一般会比较大。

二、使用 selenium 操作Chrome 浏览器

先确定一下系统中的chrome浏览器的版本,由于selenium 操作Chrome浏览器需要使用到ChromeDriver,不同版本的Chrome对应着不同的ChromeDriver,如果不对应的话可能会失败。

然后到 https://chromedriver.chromium.org/downloads 这里下载对应的ChromeDriver,国内的用户如果访问不了的话可以使用淘宝镜像源 https://npm.taobao.org/mirrors/chromedriver

需要将chromedriver放到环境变量里,如果不放到环境变量里的话,也可以在代码中指定chromedriver的路径

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time

driver = webdriver.Chrome()
# webdriver.Chrome 在初始化的时候也可以指定chromedriver路径,这种对于系统中有多个版本的chromedriver非常有用
# driver = webdriver.Chrome(executable_path="/Users/qihoo/Desktop/chromedriver94")
driver.get("https://www.google.com")
assert "Google" in driver.title
elem = driver.find_element_by_name("q")
elem.clear()
elem.send_keys("360")
elem.send_keys(Keys.RETURN)
assert "No results found." not in driver.page_source
driver.close()

image-20211014195859378

三、使用 selenium 操作 electron 应用

想要使用 selenium 操作electron app, 首先要确认的是该app使用的是哪个版本的Chromium

那么该如何确认electron应用的Chromium版本号呢?

一般情况下,我们是测试的app都是由开发打包之后的app,对于这种应用,我们很难在代码上做些hook,如果我们打开chrome devTools面板,(mac下使用option+command+i), 在console标签中输入 process.versions 可以查看到 chrome 版本,那么最好不过了,可以直接去下载对应的chromedriver, 但是更多的时候这个 process 对象是没有定义的,如果想要在控制台使用process,在编写app时,应该加上

1
2
3
4
5
6
7
8
9
function createWindow () {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: true, contextIsolation: false //加上这个配置才可以在控制台使用process对象
    }
  })

image-20211014172936368

更多的时候我们可以随便找一下版本的 chromedriver, 在初始化的过程中,如果程序报错了,会提示出app用的是哪个版本的chrome

selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 94
Current browser version is 80.0.3987.86 with binary path /Applications/360家.app/Contents/MacOS/360家

这里有显示我当前的 chromedriver 版本号是94,但是要被测试的应用的版本号是80,所以就知道了需要下载哪个版本的 chromedriver了。

我们使用electron来创建一个mac 应用,代码使用官方提供的示例,https://www.electronjs.org/zh/docs/latest/tutorial/quick-start , 但是打包我这里和官方的不太一样

  1. 创建项目
1
2
3
mkdir electroltest 
cd electrontest
npm init -y
  1. 安装electron 和相应的打包工具
1
npm install electron electron-packager electron-installer-dmg --save
  1. 修改 package.json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
  "name": "electroltest",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",  //修改为main.js 
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "electron .", // 添加一个启动指令
    "build": "electron-packager .", //添加打包指令
    "dmg": "electron-installer-dmg ./electroltest-darwin-x64/electroltest.app/ electroltest" // 添加生成dmg指令
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "electron": "^15.1.2",
    "electron-installer-dmg": "^3.0.0",
    "electron-packager": "^15.4.0"
  }
}

  1. 添加 main.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const { app, BrowserWindow } = require('electron')
const path = require('path')

function createWindow () {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  win.loadFile('index.html')
}

app.whenReady().then(() => {
  createWindow()

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow()
    }
  })
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})


  1. 添加index.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Hello YYX!</title>
    <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
</head>
<body>
    <h1>Hello 杨彦星!</h1>
    <p>
        We are using Node.js <span id="node-version"></span>,
        Chromium <span id="chrome-version"></span>,
        and Electron <span id="electron-version"></span>.
    </p>
</body>
</html>

  1. 添加 proload.js
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
window.addEventListener('DOMContentLoaded', () => {
    const replaceText = (selector, text) => {
      const element = document.getElementById(selector)
      if (element) element.innerText = text
    }
  
    for (const type of ['chrome', 'node', 'electron']) {
      replaceText(`${type}-version`, process.versions[type])
    }
  })

写完代码以后可以使用npm run start 看下效果,是否可以正常的显示electron应用

image-20211014184536826

如果正常显示则可以进行打包操作

  1. 打包electron应用

这里只是将electron 应用打成app,并没有生成dmg安装包

1
npm run build   #这里是由于在package.json中定义了build指令,如果没有定义也可以运行 npx electron-packager .
  1. 生成mac下的 dmg 安装包
1
npm run dmg  # 同build指令,这里也可以使用 npx electron-installer-dmg ./electroltest-darwin-x64/electroltest.app/ electroltest

如果一切顺利,则会在当前目录下生成 electroltest.dmg 文件 ,双击这个文件就可以安装在mac中了

image-20211014184855362

接下来我们来写一个简单的测试,就是打开app,查看一下html,再跳转到google,输入一些文字,再点击搜素按钮

该electron 应用安装的位置是

1
/Applications/electroltest.app/Contents/MacOS/electroltest

自己根据安装的位置进行相应的修改

  1. 安装 selenium
1
pip install selenium
  1. 写测试用例
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name:     eletest
   Description :  electrol自动化测试
   Author :       杨彦星
   date:          2021/10/14
-------------------------------------------------
"""

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time


options = webdriver.ChromeOptions()
options.binary_location = "/Applications/electroltest.app/Contents/MacOS/electroltest"
driver = webdriver.Chrome(chrome_options=options)
time.sleep(3)
print(driver.page_source)
driver.get("http://www.google.com")
elem = driver.find_element_by_name("q")
elem.clear()
elem.send_keys("360") # 这里也可以输入中文
elem.send_keys(Keys.RETURN)
driver.close()
driver.quit()


这里比较重要的是定义 options.binary_location 需要设置为 electron 应用的路径

20211014190302

四、使用 selenium 操作已经打开的electron应用

通过以前的方式我们其实是重新打开了某个app, 但是更多的时候,我们是期望测试一个已经打开的app,试想一下如下的场景,某个app需要登录,登录的方式又比较复杂,扫码登录或者短信登录,或者有验证码,但是如果成功登录以后,一段时间以后再次打开就不会进入到登录界面,此时,我们不希望每次运行用例的时候,都重新登录,这时,我们就需要使用 selenium 来测试一个打开着的应用。

使用 electron 构建的app, 在启动的时候可以加上一个用于调试的端口

1
/Applications/electroltest.app/Contents/MacOS/electroltest --remote-debugging-port=9222

这样就可以正常的启动应用的同时,也会开启远程调试功能,我们在使用selenium时,就可以直接对该app进行测试了而不需要再次重新打开一个新的app

这里的端口需要一个没有被占用的端口,初始化driver时可以

1
2
3
options = webdriver.ChromeOptions()
options.add_experimental_option("debuggerAddress", "127.0.0.1:9222") # 设置debuggerAddress
driver = webdriver.Chrome(chrome_options=options)

通过上面的设置以后,我们就可以直接在打开着的应用上进行测试了,非常适合那种登录比较复杂的应用。

五、总结

使用 selenium 测试 electron 应用的测试有几个几点总结

  1. 找到对应的 chromedriver
  2. 通过 binary_location 参数设置app路径
  3. 对于测试打开着的app, 需要在app启动的时候添加远程调试参数 --remote-debugging-port

参考

Selenium-Python中文文档

打包 electron 应用到 mac