本文内容涉及ES6 async
、jest
的相关知识,对于以上内容不太了解的读者能够先了解相关内容。javascript
它由Chrome官方团队提供,经过Devtools协议在Node层提供了一系列API来控制chrome或者chromium,也就是说咱们可以编写Node环境的代码便可对浏览器的行为进行控制。经过它咱们能够作到如下行为:java
生成页面快照:图片、pdfnode
抓取spa应用生成预渲染页面git
自动化表单提交、UI测试、键盘输入github
抓取应用的性能数据(chrome performance timeline)chrome
测试chrome扩展shell
固然以上行为都是puppeteer能力中极小的一部分,更多的读者能够阅读Puppeteer的文档来了解,本文将介绍Puppeteer在E2E测试的实践api
简单来讲,就是模拟真实用户使用场景进行测试,预期应用可以正常响应用户的操做,其关键点在于模拟用户使用环境,模拟用户操做。promise
那对于Web应用来讲,用户环境就是浏览器,用户操做主要是移动、点击,这些就是咱们须要模拟的部分,下面就直接进入环境和实践部分。浏览器
本文的例子采用puppeteer
jest
jest-puppeteer
在mac环境实现
首先须要安装以上依赖,这里须要注意如下问题:
puppeteer会默认下载chromium,这里能够经过export PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
跳过chromium下载
Node版本最好使用大于8的版本
接下来讲明一下相关的配置
首先是jest相关的配置,在根目录建立jest.config.js
//jest.config.js
const config = require('config');
const _ = require('lodash');
module.exports = {
preset: 'jest-puppeteer', //调用preset
globals: _.assign({}, config.get('e2e.variable'), { //这里能够注入全局变量
ENV_URL: config.get('baseUrl')
}),
testMatch: ['**/__e2e__/**/*.test.js?(x)'] //指定须要进行测试的文件
};
复制代码
接下来就是配置puppeteer,在根目录建立jest-puppeteer.config.js
//jest-puppeteer.config.js
module.exports = {
launch: {
headless: true, //设定运行模式,false的状况下将会工做在有GUI界面的模式,true则不开启GUI界面
executablePath: //设定本地Chrome路径,官方推荐使用Chrome Canary
'/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary'
}
};
复制代码
环境已经配置好了,是时候进入实践环节了。对于绝大部分系统尤为是管理系统来讲,第一步要作的确定是登陆系统,所以,咱们的第一个实践就是用puppeteer脚本模拟登陆,这里的例子基于做者这边的一个系统的登陆流程。
const loginFunc = page => async () => {
await page.setViewport({ width: 1280, height: 720 }); //设置窗口大小
await page.goto(`${ENV_URL}/login`); //前往登陆地址
const loginIframe = page.frames().find(f => f.name() === 'login-iframe'); //找到登陆iframe
await loginIframe.waitForSelector(
'#login-tab-container > div.tab-item.tab-right'
);
await loginIframe.click('#login-tab-container > div.tab-item.tab-right'); //切换登陆TAB
await page.waitFor(1000); //特殊用途,以后说明
const username = await loginIframe.waitForSelector('#username'); //找到输入框
await username.type(USERNAME); //输入用户名
const pass = await loginIframe.waitForSelector('#password');
await pass.type(PASSWORD); //输入密码
await loginIframe.click('#login-btn'); //点击登陆按钮
await page.waitForNavigation(); //等待跳转导航完成
};
module.exports = {
loginFunc: loginFunc
};
复制代码
在这个例子中,展现了打开登陆页面,输入用户名、密码并进行登陆的流程,主要涉及到了setViewport
goto
frames
waitForSelector
click
waitFor
type
waitForNavigation
这些API,下面说说其中几个比较重要的API,更多的能够参考官方文档;
setViewport
设置窗口大小,返回一个promise
waitForSelector
参数为CSS Selector,返回一个promise,直到指定的元素出现才会resolve,超时后会reject
click
点击某个元素,返回一个promise
type
向某个元素输入内容,返回一个promise
waitForNavigation
URL改变时触发,返回promise,导航结束时resolve,经过history api进行URL更改时也能够经过该方法等待导航结束
frames
能够获取页面中全部的iframe
复制代码
相信读者看完这段例子后也能感觉到puppeteer的API设计是十分语意化的,很是好懂。可是实际上这个例子是存在几个踩坑点的,这里说一下笔者从这个例子中收获的经验:
waitForSelector只有当目标Selector本来不存在DOM中才会生效,若是目标元素是经过display: none;
visibility: hidden
这类方式进行切换的话,这个方法是直接resolve的。
若是这个切换过程当中还存在动画效果,那在这个动画效果的过程当中,不管是click仍是type动画过程当中的元素,操做都是不会生效的,必须等到动画结束,所以在本例子中还增长了waitFor(1000)
这句代码来等待动画的结束
至此咱们已经完成了登陆,接下来就能够和jest结合进行自动化测试了,先放出本部分的例子:
const utils = require('./utils');
jest.setTimeout(10000);
describe('e2e test', () => {
beforeAll(utils.loginFunc(page));
it('e2e test-1', async () => {
const el = await page.waitForSelector(
'#root > .services_C2FC97 > .ant-row > .ant-col-8:nth-child(1) > .service-card_C2FC97'
);
expect(await el.$eval('p', node => node.innerText)).toBe('test'); //获取指定元素的innerText
});
});
复制代码
在这个例子里,咱们先引入了上面编写的loginFunc来做为每个测试的前置条件,因为使用了jest-puppeteer,运行环境中会自动注入puppeteer的page和browser对象,所以能够直接调用;这个例子中测试的是首页的一张功能卡片的标题是否为test
;这个例子是很基础的,不过相信经过上面那个登陆的例子,读者已经了解到编写E2E测试脚本的套路了。
不过这里还有一点须要说明,因为puppeteer启动时间和打开网页的耗时比较难以估计,须要经过jest.setTimeout(ms)
来延长一个测试执行的时间,不然有可能由于执行超时致使失败,下面是这个例子最终的执行结果:
yarn e2e
yarn run v1.9.4
$ cross-env NODE_ENV=test jest -c jest.config.js
Determining test suites to run...
DevTools listening on ws://127.0.0.1:54751/devtools/browser/7d8d289a-5335-4ffc-a65c-f7e98cb34de0
[1129/154120.584773:WARNING:spdy_session.cc(3152)] Received HEADERS for invalid stream 25
PASS __e2e__/demo.test.js (6.931s)
e2e test
✓ e2e test-1 (1540ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 7.046s
Ran all test suites.
✨ Done in 10.72s.
复制代码
至此,入门教程就结束了,是否感受到很像按键精灵?但实际上经过puppeteer咱们能够实现更强大的一些控制,包括监听网络请求,监听页面建立等等,基本上咱们能在浏览器手动作到的,经过它提供的Node API也可以作到,这部份内容欢迎读者去阅读文档进一步的进行探索;
那E2E测试究竟能为咱们带来什么呢?在笔者看来,对于一个长期迭代的项目,随着项目规模的扩大,功能的回归测试耗费的时间会不可避免的增长,在这种状况下,若是有自动化测试可以帮咱们进行部分测试,将大大提升咱们的效率;同时经过定时测试任务能够更早的发现产品功能上存在的问题,从而保证产品可以按时交付,这是它带来的最大的价值。
最后,推荐给你们一个小工具puppeteer-recorder
,这是一个Chrome插件,能够方便的录制测试脚本,从而摆脱手动编写脚本的苦恼😄(因为同源策略,该工具不支持iframe内的操做录制)
@author: Monado