Edeity's Blog

前端自动化测试探索

众所周知,在迭代过程中会引入各种问题,为了尽可能避免或减少问题,尤其是曾发现的问题,要进行回归测试。

函数库,因为有严格的输入输出,容易写单元测试;前端业务的问题,因老板关注,投入了大量测试资源,多层次测试;但性能上的问题,则相对晦涩。曾有一个性能问题,在被无意间发现时,已在线上存在N月。因此,一个产品越复杂,迭代越频繁,越有必要提供能面向整个开发流程的自动化测试工具。

  • 要尽可能操作浏览器,及时获取运行信息
  • 要提供非代码的操作方式,说白了,就是图形界面

只有在满足以上两者后,我们才可以逐渐完善诸如冒烟测试的测试用例,也可以约定指标,量化前端特性。

针对前端业务,经过一定时间的了解,目前有一种不成熟的解题思路:puppeteer + jenkins 以供参考。

Jenkins

简介

Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台

最常用的功能,根据某种条件(如提交代码、定时、用户点击等),运行各类脚本或软件,记录运行日志,最终要的是提供图形界面的软件。

安装

以windows为例(公司的电脑只给配了windows),下载最新版的jenkins.war,安装运行.war所需的JAVA环境

  • 因为网络不佳,所以推荐用清华源下载Jenkins以及其插件。
  • JAVA下载过慢,也可以到腾讯应用用心搜索JRE

以8080为端口,运行程序:

java -jar ./your/path/to/jenkins.war --httpPort=8080

值得注意,虽然jenkins是通过清华源下载的,但默认的插件安装,仍然以官方地址下载,所以很卡或干脆安装不成功,故同样需要换成清华源。具体做法,运行后,Jenkins会在用户目录(如C:\Users\admin\)创建.jenkins文件夹,里面保存了相关配置,打开hudson.model.UpdateCenter,将其中的

<url>https://updates.jenkins.io/update-center.json</url>

替换为

<url>https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json</url>

再次运行jenkins.war,我们会看到在.jenkins/updates/下多了default.json文件。但此时也只是把下载了插件的地址(这些地址还是官方网址),故需要打开此文件,全局替换:

http://www.google.com 
-> http://www.baidu.com

http://updates.jenkins-ci.org/download
-> https://mirrors.tuna.tsinghua.edu.cn/jenkins

再次运行jenkins.war,打开http://localhost:8080,按照里面的提示操作即可顺利安装插件。

其他

  • 为避免权限不足,可能需要以管理员身份运行shell,进而启动jenkins。(比如无法找到cnpm命令)
  • jenkins在window下默认以cmd运行,若要更改成shell(比如:git自带的cygwin),可以到Manage Jenkins-> Config Systems ->Shell中说明本地shell的路径: C:\Program Files\Git\bin\sh.exe
  • 运行控制台输出时,默认的JAVA环境未能识别中文,呈现乱码,这不是jenkins的问题,需要添加JAVA的环境变量
    • JAVA_TOOL_OPTIONS-Dfile.encoding=UTF-8

puppeteer

Jenkins为非开发者提供了图形界面接口,但测试用例的编写才是根本。如何能尽可能模拟用户的操作?还用问,当然是用浏览器啦。但不是用手,那是测试的事,像我这样风度翩翩的前端er,用puppeteerpuppeteer是一组API,封装devtools-protocol远程调用协议,能够简单粗暴地操作Chromium。

API设计得非常凝练,短短几句话就能模拟用户操作,参照官网实例:

const puppeteer = require('puppeteer');

(async () => {
// 启动Chromium
const browser = await puppeteer.launch();
// 新建一个页面
const page = await browser.newPage();
// 打开百度
await page.goto('https://www.baidu.com');
// 模拟用户输入`Hello World`
await page.keyboard.type('Hello World');
// 对当前页面进行截图,并保存为example.png
await page.screenshot({path: 'example.png'});
// 关闭浏览器
await browser.close();
})();

当然,puppeteer支持以无头(没有界面)的形式运行。这样便可以神不知鬼不觉地在后台跑各种任务了。

const browser = await puppeteer.launch({headless: false});

更近一步

如何使用puppeteer,可以直接查看官方API:

  • 模拟用户操作(键盘输入:keyboard.type,鼠标点击:mouse.down
  • 在浏览器中运行JS,并获取信息:(page.evalute)
  • 性能跟踪:tracing.start
  • ….

只有你想不到,没有你做不到。当然,假如目前提供的API,还不够风骚刺激,也可以通过底层协议进行更深入的操作(怀疑博主在ghs),eg:

// 勾搭上chromium
const client = await page.target().createCDPSession();

// 调用Animation.enable方法
await client.send('Animation.enable');

// 运行Animation.enable后,监听Animation.animationCreated事件
client.on('Animation.animationCreated', () => {
console.log('Animation created!'));
}

当然,要注意一点,别把事件(Events)变操作(Methods),比如:

client.send('Animation.animationCreated'); // no such method

其他

当然,使用puppeteer的同时,也会用到其它的类库,仅列于下供参考:

  1. resemblejs:对两张图片进行差异对比。(比如,可以将主干的一系列操作截图视为正确结果,从而对比分支相同操作截图,发现差异)
    • screenshot截图时,耗时100ms~1s,所以不适宜截图衡量性能,ms级别的操作,仍旧推荐使用埋点
  2. nodemail: 发邮件
  3. axios:请求接口
  4. mochai + chai:写单元测试用例
  5. ….

总结

目前,这个解决方案正由本猿在本小组推行使用,有更多的结论,我还会回来记(zhuang)录(bi)的!