Puppeteer前端自动化测试实践
本篇内容将记录并介绍使用Puppeteer进行自动化网页测试,并依靠约定来避免反复修改测试用例的方案。主要解决页面众多时,修改代码导致的牵连错误无法被发现的运行时问题。文章首发于个人博客。
对前端感兴趣希望一起讨论的可以加我vx:w554091944
起因
目前我们在持续开发着一个几十个页面,十万+行代码的项目,随着产品的更迭,总会出现这样的问题。在对某些业务逻辑或者功能进行添加或者修改的时候(尤其是通用逻辑),这些通用的逻辑或者组件往往会牵扯到一些其他地方的问题。由于测试人员受限,我们很难在完成一个模块单元后,对所有功能重新测试一遍。
同时,由于环境及数据的区别,(以及在开发过程中对代码完备性的疏忽),代码会在某些特殊数据的解析和和展示上出现问题,在开发和测试中很难去发现。总的来说,我们希望有一个这样的工具,帮我们解决上述几个问题:
- 在进行代码和功能改动后,能够自动访问各个功能的页面,检测问题
- 针对大量的数据内容,进行批量访问,检测对于不同数据的展示是否存在问题
- 测试与代码功能尽量不耦合,避免每次上新功能都需要对测试用例进行修改,维护成本太大
- 定期的测试任务,及时发现数据平台针对新数据的展示完备性
其中,最重要的问题,就是将测试代码与功能解耦,避免每次迭代和修改都需要追加新的测试用例。我们如何做到这一点呢?首先我们来梳理下测试平台的功能。
功能设定
由于我们的平台主要是进行数据展示,所以我们在测试过程中,主要以日常的展示数据为重心即可,针对一些复杂的表单操作先不予处理。针对上述的几个问题,我们针对自动化测试工具的功能如下:
- 依次访问各个页面
- 访问各个页面的具体内容,如时间切换、选项卡切换、分页切换、表格展开行等等
- 针对数据表格中的详情链接,选择前100条进行访问,并进行下钻页的继续测试
- 捕获在页面中的错误请求
- 对错误信息进行捕获,统计和上报
根据以上的梳理,我们可以把整个应用分为几个测试单元
- 页面单元,检测各功能页面访问的稳定性
- 详情页单元,根据页面的数据列表,进行批量的详情页跳转,检测不同参数下详情页的稳定性
- 功能单元,用于检测页面和详情页各种展示类型点击切换后是否产生错误
通过这样的划分,我们针对各个单元进行具体的测试逻辑书写用例,这样就可以避免再添加新功能和页面时,频繁对测试用例进行修改了。
Puppeteer
带着上面我们的需求,我们来看下Puppeteer的功能和特性,是否能够满足我们的要求。
Puppeteer是一个Node库,它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome。Puppeteer 默认以 headless 模式运行,但是可以通过修改配置文件运行“有头”模式。
我们可以使用Puppeteer完成以下工作:
- 访问页面,进行截图
- 自动进行键盘输入,提交表单
- 模拟点击等用户操作
- 等等等等。。
我们来通过一些小案例,来介绍他们的基本功能:
访问一个带有ba认证的网站
puppeteer可以创建page实例,并使用goto方法进行页面访问,page包含一系列方法,可以对页面进行各种操作。
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// ba认证
await page.authenticate({
username,
password
});
// 访问页面
await page.goto('https://example.com');
// 进行截图
await page.screenshot({path: 'example.png'});
await browser.close();
})();
访问登陆页面,并进行登录
首先,对于SPA(单页面应用),我们都知道,当页面进入后,客户端代码才开始进行渲染工作。我们需要等到页面内容渲染完成后,再进行对应的操作。我们有以下几种方法来使用
waitUntil
puppeteer针对页面的访问,切换等,提供了waitUntil参数,来确定满足什么条件才认为页面跳转完成。包括以下事件:
- load - 页面的load事件触发时
- domcontentloaded - 页面的DOMContentLoaded事件触发时
- networkidle0 - 不再有网络连接时触发(至少500毫秒后)
- networkidle2 - 只有2个网络连接时触发(至少500毫秒后)
通过waitUnitl,我们可以当页面请求都完成之后,确定页面已经访问完成。
waitFor
waitFor方法可以在指定动作完成后才进行resolve
// wait for selector
await page.waitFor('.foo');
// wait for 1 second
await page.waitFor(1000);
// wait for predicate
await page.waitFor(() => !!document.querySelector('.foo'));
我们可以利用waitForSelector方法,当登录框渲染成功后,才进行登录操作
// 等待密码输入框渲染
await page.waitFor('#password');
// 输入用户名
await page.type('input#username', "username");
// 输入密码
await page.type('input#password', "testpass");
// 点击登录按钮
await Promise.all([
page.waitForNavigation(), // 等跳转完成后resolve
page.click('button.login-button'), // 点击该链接将间接导致导航(跳转)
]);
await page.waitFor(2000)
// 获取cookies
const cookies = await page.cookies()
针对列表内容里的链接进行批量访问
主要利用到page实例的选择器功能
const table = await page.$('.table')
const links = await table.$$eval('a.link-detail', links =>
links.map(link => link.href)
);
// 循环访问links
...
进行错误和访问监听
puppeteer可以监听在页面访问过程中的报错,请求等等,这样我们就可以捕获到页面的访问错误并进行上报啦,这也是我们进行测试需要的基本功能~
// 当发生页面js代码没有捕获的异常时触发。
page.on('pagerror', () => {})
// 当页面崩溃时触发。
page.on('error', () => {})
// 当页面发送一个请求时触发
page.on('request')
// 当页面的某个请求接收到对应的 response 时触发。
page.on('response')
通过以上的几个小案例,我们发现Puppeteer的功能非常强大,完全能够满足我们以上的对页面进行自动访问的需求。接下来,我们针对我们的测试单元进行个单元用例的书写
最终功能
通过我们上面对测试单元的规划,我们可以规划一下我们的测试路径
访问网站 -> 登陆 -> 访问页面1 -> 进行基本单元测试 -> 获取详情页跳转链接 -> 依次访问详情页 -> 进行基本单元测试
-> 访问页面2 ...
所以,我们可以拆分出几个大类,和几个测试单元,来进行各项测试
// 包含基本的测试方法,log输出等
class Base {}
// 详情页单元,进行一些基本的单元测试
class PageDetal extends Base {}
// 页面单元,进行基本的单元测试,并获取并依次访问详情页
class Page extends PageDetal {}
// 进行登录等操作,并依次访问页面单元进行测试
class Root extends Base {}
同时,我们如何在功能页面变化时,跟踪到测试的变化呢,我们可以针对我们测试的功能,为其添加自定义标签test-role,测试时,根据自定义标签进行测试逻辑的编写。
例如针对时间切换单元,我们做一下简单的介绍:
// 1. 获取测试单元的元素
const timeSwitch = await page.$('[test-role="time-switch"]');
// 若页面没有timeSwitch, 则不用进行测试
if (!timeSwitch) return
// 2. time switch的切换按钮
const buttons = timeSwitch.$$('.time-switch-button')
// 3. 对按钮进行循环点击
for (let i = 0; i < buttons.length; i++) {
const button = buttons[i]
// 点击按钮
await button.click()
// 重点! 等待对应的内容出现时,才认定页面访问成功
try {
await page.waitFor('[test-role="time-switch-content"]')
} catch (error) {
reportError (error)
}
// 截图
await page.screenshot()
}
上面只是进行了一个简单的访问内容测试,我们可以根据我们的用例单元书写各自的测试逻辑,在我们日常开发时,只需要对需要测试的内容,加上对应的test-role即可。
总结
根据以上的功能划分,我们很好的将一整个应用拆分成各个测试单元进行单元测试。需要注意的是,我们目前仅仅是对页面的可访问性进行测试,仅仅验证当用户进行各种操作,访问各个页面单元时页面是否会出错。并没有对页面的具体展示效果进行测试,这样会和页面的功能内容耦合起来,就需要单独的测试用例的编写了。
来源:https://segmentfault.com/a/1190000018222483
Puppeteer前端自动化测试实践的更多相关文章
- web前端自动化测试/爬虫利器puppeteer介绍
web前端自动化测试/爬虫利器puppeteer介绍 Intro Chrome59(linux.macos). Chrome60(windows)之后,Chrome自带headless(无界面)模式很 ...
- Web前端自动化测试Cypress实践总结
本文主要首先主要介绍了什么是自动化测试,接着对常用的自动化测试框架进行了对比分析,最后,介绍了如果将自动化测试框架Cypress运用在项目中. 一.自动化测试概述 为了保障软件质量,并减少重复性的测试 ...
- Lego-美团点评接口自动化测试实践
Lego-美团点评接口自动化测试实践 2018-02-07 转自:Lego-美团点评接口自动化测试实践 目录 一.概述 1.1 接口自动化概述 1.2 提高ROI 针对“减少投入成本” ...
- 前端自动化测试工具doh学习总结(二)
一.robot简介 robot是dojo框架中用来进行前端自动化测试的工具,doh主要目的在于单元测试,而robot可以用来模仿用户操作来测试UI.总所周知,Selenium也是一款比较流行的前端自动 ...
- 前端自动化测试工具doh学习总结(一)
前言 项目中需要用到前端自动化测试,自己被当作一个探针研究了下目前用的比较多的web自动化测试工具.一开始研究的是的selenium,但由于项目使用了大量的dijit控件,写起testCase来很费劲 ...
- 分享——张南《从Desktop到Mobile的自动化测试实践》
张南在top100summit(14年)上分享了google从Desktop到Mobile的自动化测试实践,这里分享一下 案例简述 随着每年移动用户量的增长,移动网络速度以及覆盖力的增强,移动设备逐年 ...
- Selenium自动化测试实践 公开班(广州)
Selenium自动化测试实践 公开班(广州) http://gdtesting.com/product.php?id=115
- 前端自动化测试漫长路之——Selenium初探
引言 最近想解决前端开发或测试中的两个问题:一是界面UI的布局适配,能否在测试的过程中,通过命令操作真机打开相应页面然后截屏,通过对图片识别分类,发现有问题的图片,然后及时修复:二是页面性能分析,很多 ...
- 有货前端 Web-APM 实践
有货前端 Web-APM 实践 0 背景 有货电商技术架构上采用的是前后端分离,前端是主要以业务展示和接口聚合为主,拥有自己的 BFF (Backend For Frontend),以 nodejs ...
随机推荐
- ubuntu下Eclipse创建Django项目
(注:部分过程可能需要FQ) Eclipse版本:Mars.x 点击help->Eclipse Marketplace,搜索“PyDev”并下载安装. 然后选择window->prefer ...
- C/C++对Lu系统内置动态对象进行运算符重载
欢迎访问Lu程序设计 C/C++对Lu系统内置动态对象进行运算符重载 1 说明 要演示本文的例子,你必须下载Lu32脚本系统.本文的例子需要lu32.dll.lu32.lib.C格式的头文件lu32. ...
- delphi中Case语法的使用方法
Case 语句If...Then…Else 语句适合选项较少的情况,如果有很多选项的话利用If 语句就比较麻烦,在这种情况下,Case 语句就容易多了.Case 语句的语法如下: case <表 ...
- java基础---->多个排序的拓展
根据一些特定的规则,我们可以利用java的反射机制做到很有趣的封装. java的排序封装 一.定义一些comparator AmountComparatorAsc:amount升序 package c ...
- 图论之最短路径(2)——Bellman-Ford算法
继续最短路径!说说Bellman—Ford算法 思路:假设起点为s,图中有n个顶点和m个边,那么它到任一点(比如i)的最短路径 最多可以有n-1条(没有回路就是n-1条):因为最短路径中不可能包含回路 ...
- Flask中的session和cookie以及日志
一.笔记一session: 首先对于session在flask中应该是加密签名的cookie,所以要先生成secret_key app.secret_key = os.environ.get('SEC ...
- SSH配置优化和慢的解决方法
author: headsen chen date: 2018-08-18 00:28:37 ssh配置优化 vim /etc/ssh/sshd_config 1,修改root端口 2,不允许ro ...
- android选择图片或拍照图片上传到服务器(包括上传参数)
From:http://blog.csdn.net/springsky_/article/details/8213898具体上传代码: 1.选择图片和上传界面,包括上传完成和异常的回调监听 [java ...
- java合并两个升序数组为一个新的有序数组
转自:http://blog.csdn.net/laozhaokun/article/details/37531247 题目:有两个有序数组a,b,现需要将其合并成一个新的有序数组. 简单的思路就是先 ...
- Mysql limit offset用法举例
转自:http://blog.csdn.net/iastro/article/details/53037600 Mysql limit offset示例 例1,假设数据库表student存在13条数据 ...