本篇内容将记录并介绍使用Puppeteer进行自动化网页测试,并依靠约定来避免反复修改测试用例的方案。主要解决页面众多时,修改代码导致的牵连错误无法被发现的运行时问题。文章首发于个人博客
对前端感兴趣希望一起讨论的可以加我vx:w554091944

起因

目前我们在持续开发着一个几十个页面,十万+行代码的项目,随着产品的更迭,总会出现这样的问题。在对某些业务逻辑或者功能进行添加或者修改的时候(尤其是通用逻辑),这些通用的逻辑或者组件往往会牵扯到一些其他地方的问题。由于测试人员受限,我们很难在完成一个模块单元后,对所有功能重新测试一遍。
同时,由于环境及数据的区别,(以及在开发过程中对代码完备性的疏忽),代码会在某些特殊数据的解析和和展示上出现问题,在开发和测试中很难去发现。总的来说,我们希望有一个这样的工具,帮我们解决上述几个问题:

  1. 在进行代码和功能改动后,能够自动访问各个功能的页面,检测问题
  2. 针对大量的数据内容,进行批量访问,检测对于不同数据的展示是否存在问题
  3. 测试与代码功能尽量不耦合,避免每次上新功能都需要对测试用例进行修改,维护成本太大
  4. 定期的测试任务,及时发现数据平台针对新数据的展示完备性

其中,最重要的问题,就是将测试代码与功能解耦,避免每次迭代和修改都需要追加新的测试用例。我们如何做到这一点呢?首先我们来梳理下测试平台的功能。

功能设定

由于我们的平台主要是进行数据展示,所以我们在测试过程中,主要以日常的展示数据为重心即可,针对一些复杂的表单操作先不予处理。针对上述的几个问题,我们针对自动化测试工具的功能如下:

  1. 依次访问各个页面
  2. 访问各个页面的具体内容,如时间切换、选项卡切换、分页切换、表格展开行等等
  3. 针对数据表格中的详情链接,选择前100条进行访问,并进行下钻页的继续测试
  4. 捕获在页面中的错误请求
  5. 对错误信息进行捕获,统计和上报

根据以上的梳理,我们可以把整个应用分为几个测试单元

  • 页面单元,检测各功能页面访问的稳定性
  • 详情页单元,根据页面的数据列表,进行批量的详情页跳转,检测不同参数下详情页的稳定性
  • 功能单元,用于检测页面和详情页各种展示类型点击切换后是否产生错误

通过这样的划分,我们针对各个单元进行具体的测试逻辑书写用例,这样就可以避免再添加新功能和页面时,频繁对测试用例进行修改了。

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前端自动化测试实践的更多相关文章

  1. web前端自动化测试/爬虫利器puppeteer介绍

    web前端自动化测试/爬虫利器puppeteer介绍 Intro Chrome59(linux.macos). Chrome60(windows)之后,Chrome自带headless(无界面)模式很 ...

  2. Web前端自动化测试Cypress实践总结

    本文主要首先主要介绍了什么是自动化测试,接着对常用的自动化测试框架进行了对比分析,最后,介绍了如果将自动化测试框架Cypress运用在项目中. 一.自动化测试概述 为了保障软件质量,并减少重复性的测试 ...

  3. Lego-美团点评接口自动化测试实践

    Lego-美团点评接口自动化测试实践 2018-02-07 转自:Lego-美团点评接口自动化测试实践 目录 一.概述  1.1 接口自动化概述  1.2 提高ROI    针对“减少投入成本”    ...

  4. 前端自动化测试工具doh学习总结(二)

    一.robot简介 robot是dojo框架中用来进行前端自动化测试的工具,doh主要目的在于单元测试,而robot可以用来模仿用户操作来测试UI.总所周知,Selenium也是一款比较流行的前端自动 ...

  5. 前端自动化测试工具doh学习总结(一)

    前言 项目中需要用到前端自动化测试,自己被当作一个探针研究了下目前用的比较多的web自动化测试工具.一开始研究的是的selenium,但由于项目使用了大量的dijit控件,写起testCase来很费劲 ...

  6. 分享——张南《从Desktop到Mobile的自动化测试实践》

    张南在top100summit(14年)上分享了google从Desktop到Mobile的自动化测试实践,这里分享一下 案例简述 随着每年移动用户量的增长,移动网络速度以及覆盖力的增强,移动设备逐年 ...

  7. Selenium自动化测试实践 公开班(广州)

    Selenium自动化测试实践 公开班(广州) http://gdtesting.com/product.php?id=115

  8. 前端自动化测试漫长路之——Selenium初探

    引言 最近想解决前端开发或测试中的两个问题:一是界面UI的布局适配,能否在测试的过程中,通过命令操作真机打开相应页面然后截屏,通过对图片识别分类,发现有问题的图片,然后及时修复:二是页面性能分析,很多 ...

  9. 有货前端 Web-APM 实践

    有货前端 Web-APM 实践 0 背景 有货电商技术架构上采用的是前后端分离,前端是主要以业务展示和接口聚合为主,拥有自己的 BFF (Backend For Frontend),以 nodejs ...

随机推荐

  1. MVC AJAX Pager Helper

    MVCPager 分页控件: Author:杨涛 http://www.webdiyer.com/mvcpager/demos/ajaxpagers/ https://yunpan.cn/cq4HDc ...

  2. MQTT的学习研究(三)moquette-mqtt 的使用之mqtt服务发布主题信息

    接着上一篇的moquette-mqtt 的使用之broker启动之后,我们需要启动moquette-mqtt 的服务端发布消息. 在moquette-mqtt 的mqtt-client中三种方式实现发 ...

  3. Docker源码分析(四):Docker Daemon之NewDaemon实现

    1. 前言 Docker的生态系统日趋完善,开发者群体也在日趋庞大,这让业界对Docker持续抱有极其乐观的态度.如今,对于广大开发者而言,使用Docker这项技术已然不是门槛,享受Docker带来的 ...

  4. Java多线程详解(二)

    评论区留下邮箱可获得<Java多线程设计模式详解> 转载请指明来源 1)后台线程 后台线程是为其他线程服务的一种线程,像JVM的垃圾回收线程就是一种后台线程.后台线程总是等到非后台线程死亡 ...

  5. 在MacOS上搭建Vulhub漏洞平台环境

    一.安装python3和docker brew install python3 brew cask install docker sudo pip3 install docker-compose 二. ...

  6. pano2vr制作360全景图

    1.下载pano2vr中文破解版2.制作360全景选择"矩形球面投影" 3.输出格式选择HTML5, 也可选择Flash(快被淘汰) 4.HTML5输出选项中3个重要选项4.1 F ...

  7. elasticsearch-1.2.1客户端连接DEMO

    1.下载elasticsearch-1.2.1的zip包,解压之后 双击bin目录中的 elasticsearch.bat(针对windows系统) 启动服务器(默认监听9200端口) 访问 http ...

  8. Promise、async、await在Egret的简单应用

    Egret Engnie 5.1.10 Egret Wing 4.1.5 一.Promise.async.await相关知识 Promise介绍 阮一峰 async函数 阮一峰 具体和详细的说明用法可 ...

  9. This function has none of DETERMINISTIC, NO SQL

    错误信息: [Err] 1418 - This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declara ...

  10. Git的配置和使用

    eclipse中Git的配置 可以参考http://www.cnblogs.com/zhxiaomiao/archive/2013/05/16/3081148.html, http://blog.cs ...