前端测试主要分五大方向测试,而这五大方向也分不少小方向测试,首先简单的介绍每一个方向的概念css
界面样式测试html
功能测试前端
多浏览器测试python
性能测试git
质量测试github
如上图所示,真正工做中没法所有知足以上条件,因此须要做出权衡,通常来讲,只须要知足如下几点,就能够对项目开展自动化测试(1.需求稳定
不会频繁变动。2.多平台运行,组合遍历型,大量的重复任务。3.软件维护周期长,有生命力。4.被测系统开发较为规范,可测试性强。):web
自动化测试最大的挑战就是需求的变化,而自动化脚本自己就须要修改、扩展、debug,去适应新的功能,若是投入产出比过低,那么自动化测试也失去了其价值和意义;chrome
折中的作法是选择相对稳定的模块和功能进行自动化测试,变更较大、需求变动较频繁的部分用手工测试;npm
测试数据、测试用例、自动化脚本的重用性和移植性较强,下降成本,提升效率和价值;api
自动化测试的需求稳定性要求、自动化框架的设计、脚本开发与调试均须要时间,这其实也是一个软件开发过程,若是项目周期较短,没有足够的时间去支持这一过程,那自动化测试也就不须要了;
主要出于这几点考虑:被测试系统的架构差别、测试技术和工具的适应性、测试人员的能力可否设计开发出适应差别的自动化测试框架;
对于界面布局,传统的测试都是由人工对比设计图和产品界面。当界面有修改以后,再由人经过肉眼去检查修改(包括正确的和错误的修改),这样即费时并且测试结果又不稳定,由于人工对比测试存在两个巨坑:1.效率低;2.人的不肯定性。对于拥有大量复杂界面的Web应用,界面布局的测试的数量巨大,再加上这两个问题,致使这类应用的界面布局测试/回归测试时间很长,成本很高,因此不少基于Agile(敏捷开发)项目基本不可能在迭代周期内高质量的完成其视觉测试。对于天天作一次,那更是不可能完成的任务。
为了解决上面提到的各类问题,视觉感知测试孕育而生。它使用传统的对图片进行二进制比较的办法,结合敏捷迭代开发的理念,产生的一种针对界面布局的自动化测试方法。
视觉感知测试就是对第一个版本的全部界面进行第一次测试。
视觉感知测试包含如下几个主要的测试步骤:
须要注意的是!
经过配对URL,对全部的截图按照相同的URL进行分组。固然有时候会出现新的界面,有时候老的界面会被删除。对于新的界面就须要人工进行首次验证测试 。
对于分组以后的截图进行像素级别的比较并生产差异图。有时候为了降噪,能够只对局部关心的组件进行比较。
最后经过人工审查差异图报告完成测试。
视觉感知测试结果:
预期(expected) | 实际(actual) | 比较结果(diff) |
---|---|---|
![]() |
![]() |
![]() |
咱们认为若是一个界面经过第一次的人工验证并发布以后,它就是一个正确的标准界面,而且是包含了人工测试价值的资产。当下一次测试的时候,这部分价值就应该被保留并重用起来,用于减小新的一次测试的时间,从而实现界面的快速回归测试。
视觉回归测试包含如下几个主要的测试步骤:
回归和感知测试流程差很少只是差别值要更小一点,而且只有效果图须要替换内容。
要进行视觉自动测试,有三种方式。
这三种各有明显的优点和不足。 第二种方式强绑定了实现,从而变得可能比较脆弱。 第一种方式离设计太近了,当页面中有可变内容时就会有问题。
第三种方式,没法进行视觉感知测试结果只能进行视觉回归测试和上一版的dom继续比较差别。
我更倾向与第一种截图对比;它的测试基于用户所见而不是用户所见的抽象。固然第三种也是很是好的 page-monitor 有兴趣的朋友能够自行了解。为何第三种那么好为何不使用呢?由于上面这个库是基于phantomjs而且它的实现方式过于复杂不适合新手玩玩。
名称 | 地址 |
---|---|
PhantomCSS | https://github.com/HuddleEng/... |
GhostStory | https://github.com/thingsinja... |
Cactus | https://github.com/winston/ca... |
Needle | https://github.com/python-nee... |
CSSCritic | https://github.com/cburgmer/c... |
sikuli | http://www.sikuli.org/ |
Mogo | http://mogotest.com/ |
pixelmatch | https://github.com/mapbox/pix... |
pixel-diff | https://github.com/koola/pixe... |
好了介绍了那么多,怎么选一个合适的Headless Browser呢?
Headless Browser???我是视觉测试要无界面浏览器干吗?
由于有了像素对比工具咱们还须要一个浏览器进行截图和设计图进行像素比较。
名称 | 内核 | 地址 |
---|---|---|
Puppeteer | Webkit | https://github.com/GoogleChro... |
PhantomJS | Webkit | http://phantomjs.org/ |
SlimerJS | Gecko | https://github.com/laurentj/s... |
TrifleJS | IE | https://github.com/sdesalas/t... |
PhantomJS 基于 Webkit 内核,不支持 Flash 的播放;SlimerJS 基于火狐的 Gecko 内核,支持 Flash播放,而且执行过程会有页面展现。
咱们这里呢就只讲Webkit内核的,其余的我就不讲了。
PhantomJS
是一个基于webkit的JavaScript API。它使用QtWebKit做为它核心浏览器的功能,使用webkit来编译解释执行JavaScript代码。任何你能够在基于webkit浏览器作的事情,它都能作到。它不只是个隐形的浏览器,提供了诸如CSS选择器、支持Web标准、DOM操做、JSON、HTML五、Canvas、SVG等,同时也提供了处理文件I/O的操做,从而使你能够向操做系统读写文件等。PhantomJS的用处可谓很是普遍,诸如网络监测、网页截屏、无需浏览器的 Web 测试、页面访问自动化等。
可是 PhantomJS
由于毕竟不是真实的用户浏览器环境,使用起来仍是有很多的诟病。以前一直在使用 PhantomJS
,功能虽然够用,不过和在真实的浏览器里面访问的界面来对比差异仍是比较大的。
Puppeteer
是Chrome团队开发的一个Node库。它提供了一个高级API来控制无头或完整的Chrome。它经过使用Chrome无界面模式 (Headless Chrome
)和DevTools
协议的组合来实现这一点。它使用一个更上层的API来封装其功能,让用户界面测试自动化变得垂手可得。
人们基于Chrome DevTools协议开发了一系列Google Chrome工具。你在浏览器中点击更多工具 ->开发工具,打开的就是DevTools。DevTools协议是DevTools的动力基础,咱们如今可使用Chrome中的DevTools来作更多的事情。
好了简介讲完了,咱们来对比一下这两个Headless Browser的区别。
PhantomJS:
var page = require('webpage').create(); page.viewportSize = { width: 400, height: 400 }; page.open("http://localhost:8899/VS", function(status) { if (status === "success") { page.render("a.jpg"); } else { console.log("Page failed to load."); } phantom.exit(0); });
Puppeteer:
const puppeteer = require('puppeteer'); (async() => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('http://localhost:8899/VS'); await page.setViewport({ width: 400, height: 400 }) //保存图片 const images = await page.screenshot({ path: 'VS.jpg', fullPage: true, omitBackground: true }); //关闭浏览器 await browser.close(); })();
Puppeteer | PhantomJS | Chrome浏览器 |
---|---|---|
![]() |
![]() |
![]() |
浏览器效果(大图):
好了看到这里已经能够分别出来胜负了。
咱们来作一个简单的dome
咱们这里拿掘金来作一个视觉感知测试的例子。
1.首先建立项目名字就叫“PerceptionTest”把。
2.安装依赖
安装Puppeteer
npm install puppeteer --save
像素对比工具我就选我最经常使用的blink-dif了
npm install blink-diff --save
3.依赖安完了咱们来建立一个js文件“app.js”
4.加载依赖
const puppeteer = require('puppeteer'),//无头浏览器 BlinkDiff = require('blink-diff'),//像素对比 imgUrl = __dirname + "/blink-diff_img/";//图片目录
5.使用puppeteer进行截图
(async () => { //建立puppeteer const browser = await puppeteer.launch({ headless: true }); //new 一个新的tab页面 const page = await browser.newPage(); //设置浏览器的尺寸 await page.setViewport({ width: 1920, height: 945 }); //打开url await page.goto('https://juejin.im/'); //保存截图 await page.screenshot({ path: imgUrl + 'Screenshots.png', fullPage: true }); //关闭浏览器 await browser.close(); })();
6.分析页面找到要替换的内容
为何要替换内容呢,由于咱们UI测试指的是测试界面样式而不是去匹配里面的内容,若是不替换里面的内容那像素对比工具比较出来的类似度确定很低。因此咱们要替换掉内容,要让内容完整统一,咱们才好更加精确的去比较差别。
好了咱们来分析一下要替换的内容。
要替换的内容以下:
7.替换内容
puppeteer提供了很是丰富的api,其中有个api叫page.evaluate能够向页面插入一段js。
await page.evaluate(async () => { //列表 var Lists = document.querySelectorAll("div.feed.welcome__feed > ul > li > div > a > div"); Lists.forEach(function (element, index, array) { element.querySelector("a.title").innerHTML = "测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试"; //替换标签 element.querySelector("ul > li.item.category > span").innerHTML = "测试"; //替换做者 element.querySelector("ul > li.item.username.clickable > div > a").innerHTML = "测试"; //替换发布时间 element.querySelector("div.info-row.meta-row > ul > li:nth-child(3)").innerHTML = "9999天前"; //替换发布时间 element.querySelector("div.info-row.meta-row > ul > li:nth-child(4)").innerHTML = "99999999999 次阅读"; //列表图片 if (element.querySelectorAll("div.lazy.thumb.thumb.loaded").length==1) { element.querySelector("div.lazy.thumb.thumb.loaded").style.background = "#fdedc9"; } else { var loaded=document.createElement("div"); loaded.className=" lazy thumb thumb loaded"; loaded.style.background = "#fdedc9"; loaded.setAttribute("data-v-b2db8566",""); loaded.setAttribute("data-v-009ea7bb",""); loaded.setAttribute("data-v-f2ca14b0",""); element.appendChild(loaded); } }); });
8.使用Blink-Diff进行像素对比较
const diff = new BlinkDiff({ imageAPath: imgUrl + 'example.png', // 设计图 imageBPath: imgUrl + 'Screenshots.png',//页面截图 //低于其中差别的像素数/ p(默认值:500) - 百分比阈值:1 = 100%,0.2 = 20% threshold: 0.02, // 1% threshold imageOutputPath: imgUrl + 'Diff.png'//Diff路径 }); diff.run(function (error, result) { if (error) { throw error; } else { console.log(diff.hasPassed(result.code) ? '经过' : '失败'); console.log('总像素:' + result.dimension); console.log('发现:' + result.differences + ' 差别.'); } });
完整代码:
const puppeteer = require('puppeteer'), BlinkDiff = require('blink-diff'), imgUrl = __dirname + "/blink-diff_img/"; (async () => { const browser = await puppeteer.launch({ headless: true }); const page = await browser.newPage(); await page.setViewport({ width: 1920, height: 945 }); await page.goto('https://juejin.im/'); await page.evaluate(async () => { //列表 var Lists = document.querySelectorAll("div.feed.welcome__feed > ul > li > div > a > div"); Lists.forEach(function (element, index, array) { element.querySelector("a.title").innerHTML = "测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试"; //替换标签 element.querySelector("ul > li.item.category > span").innerHTML = "测试"; //替换做者 element.querySelector("ul > li.item.username.clickable > div > a").innerHTML = "测试"; //替换发布时间 element.querySelector("div.info-row.meta-row > ul > li:nth-child(3)").innerHTML = "9999天前"; //替换发布时间 element.querySelector("div.info-row.meta-row > ul > li:nth-child(4)").innerHTML = "99999999999 次阅读"; //列表图片 if (element.querySelectorAll("div.lazy.thumb.thumb.loaded").length==1) { element.querySelector("div.lazy.thumb.thumb.loaded").style.background = "#fdedc9"; } else { var loaded=document.createElement("div"); loaded.className=" lazy thumb thumb loaded"; loaded.style.background = "#fdedc9"; loaded.setAttribute("data-v-b2db8566",""); loaded.setAttribute("data-v-009ea7bb",""); loaded.setAttribute("data-v-f2ca14b0",""); element.appendChild(loaded); } }); }); await page.screenshot({ path: imgUrl + 'Screenshots.png', fullPage: true }); const diff = new BlinkDiff({ imageAPath: imgUrl + 'example.png', // 设计图 imageBPath: imgUrl + 'Screenshots.png',//页面截图 threshold: 0.02, // 1% threshold imageOutputPath: imgUrl + 'Diff.png'//Diff路径 }); diff.run(function (error, result) { if (error) { throw error; } else { console.log(diff.hasPassed(result.code) ? '经过' : '失败'); console.log('总像素:' + result.dimension); console.log('发现:' + result.differences + ' 差别.'); } }); //关闭puppeteer await browser.close(); })();
差别图
有差别 | 无差别 |
---|---|
![]() |
![]() |
git完整代码:https://github.com/my07ke/Per...
好了,欲知后事如何,请听下回分解。
点击连接加入群聊【前端|WEB|CSS|Javascript|HTML】:https://jq.qq.com/?_wv=1027&k...