项目的开发流程中有一个重要的环节UI走查,当前端开发人员还原样式之后,UI会对照他画的设计稿来比对页面的还原情况。 我所见的常态是总会有略微调整。可能边距差了2px,可能高度高了3px。十分考验UI的眼力,跟玩大家来找茬一样。如果这样的走查环节出现失误,在线上发生了一些误差,那么影响是非常不好的。在我看来线上发生的几率很高,毕竟光靠眼睛来比对的话达不到100%无误。
所以,为了解决web开发中发生的如上现象,[backstopJs]进入了我们的视野。
backstop官方给予的介绍如下
BackstopJS automates visual regression testing of your responsive web UI by comparing DOM screenshots over time.
BackstopJS通过比较一段时间内的DOM截图,自动化了对响应web UI的可视化回归测试。
通过介绍可知,backstop解决了自动化UI
回归测试的问题。主要面对场景为web响应式
。那么我们继续往下,实际上手使用一下,看看它能带给我们什么样的惊喜。
讲解的顺序按照实际开发顺序讲解
全局安装backstopJs
$ npm install -g backstopjs 复制代码
全局安装完毕后,控制台打入backstop命令可以看到如下描述,那么就说明安装成功了。文章中使用的是BackstopJS 5.1.0
版本 CLI
创建一个文件夹,我这里文件夹的命名为backstop-article
$ mkdir backstop-article && cd backstop-article 复制代码
进入创建的文件夹后,执行backstopJs init
命令,初始化基础项目结构。
$ backstop init 复制代码
初始化后终端显示如下。
接下来,打开项目,我们看一下目录结构。
首先分析一下目录结构
|- backstop_data // 数据文件 |- engine_scripts // 核心配置文件 |- puppet |- clickAndHoverHelper.js // 一些脚本流程操作,如:click、hover、interaction等等 |- ignoreCSP.js // 屏蔽CSP安全策略(下方有详解) |- interceptImages.js // 拦截图片地址,更换图片图片执行文件。配置不在此处 |- loadCookies.js // 加载cookie执行文件。 |- onBefore.js // 页面加载之前执行的脚本 |- onReady.js // 页面加载之后执行的脚本 |- overrideCSS.js // 样式覆盖执行文件 |- cookies.json // cookie配置文件 |- imageStub.jpg // 图片替换的占位图 |- backstop.json // backstopJs配置文件 复制代码
这里稍微科普一下网页安全政策Content Security Policy
,缩写 CSP
的来历,CSP的诞生是为了防止XSS
攻击。
CSP
的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单
。它的实现和执行全部由浏览器完成,开发者只需提供配置
。CSP
大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。
两种方法可以启用CSP
。一种是通过HTTP
头信息的Content-Security-Policy
的字段。
上述图片捕获自Github网站
另一种是通过网页的<meta>
标签。
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:"> 复制代码
上面的介绍和简介简单的看一下即可。回到backstopJs
中,回想一下ignoreCSP.js
文件名字也就不难理解为什么会有这个文件了,屏蔽CSP是必要的,否则可能没法拿到一些页面的数据、资源等。
接下来我们执行一下
$ backstop test 复制代码
执行完后会弹出一个页面,如下图所示:
可以看出最上方有一些筛选操作,可以按照比对成功筛选、失败筛选、模糊筛选
,右上角设置按钮可以调整下方对比卡片的展示内容。
接下来我们来看展示内容卡片。卡片左上角展示了当前卡片的基本信息。
在卡片中心部门分为了三大块 reference-参照图片
、test-测试图片
、diff-比对图片
,但是第一次启动的时候,你所看到的页面和上方的截图是一样的,只有测试图片。下方提示你“当前没有找到参照图片”
。因为没有reference参照图片
,所以也有没有右侧的diff对比图片
。
这个时候看一下目录结构。
目录结构相对最开始的时候发生了一些变化,多出了两个文件夹
简单看了一下弹出的页面后,我们回头看一下刚刚执行时候的控制台最下方有一段Error
报错的为这两句
compare | Reference image not found backstop_default_BackstopJS_Homepage_0__0_phone.png compare | Reference image not found backstop_default_BackstopJS_Homepage_0__1_tablet.png 复制代码
比对步骤的时候 | 未找到参照图片,图片名称为 backstop_default_BackstopJS_Homepage_0__0_phone.png
,命名分别是测试名字_测试用例ID_选择器_屏幕尺寸
如何创建reference参照图片
呢,有下面几种方式:
如果当前截图是你认为的参照模板,那么执行这条语句后,当前的测试图片就会存为参照图片。这个模式通常用于批量生成参照图片使用。如果UI设计可以批量导入到另一个网址的时候。不过我们公司没有这种网站,可以考虑搭建一个。
最开始的时候用UI设计库的网址批量生成reference参照图片
,然后参照图片生成完毕后,把地址换成开发环境地址,也就是我们还原后的项目地址。这样的话就可以进行UI及开发web之间的比对了。具体操作如下:
执行backstop approve
$ backstop approve 复制代码
执行完毕
这时候我们再看一下目录结构,会发现多了一个backstop_reference
文件夹,创建规则bitmaps_test/<timestamp>/
backstop_reference
文件夹内部则是刚刚测试地址的两张尺寸的图片。那么这个时候项目中就有了参照图片,再执行测试命令的话就可以看到如下完整界面。
参照图片与测试图片比对通过,没有diff对比图片
。因为我们的对照图片就是从测试地址上截取
下来的,所以是一模一样的。这个时候如果把测试地址跟换一下的话,就可以看到diff图片了。
上面的参照图片还是我们approve
的官网图片,测试地址我更换了一下头图,和第二段中的几个文字颜色。可以看出在diff对比图片中,会用高亮
覆盖的方式展示差异,这样的话就完成了一次完整的比对。(图片中红色的框是我画的,为了突出修改区域)。
如果恰巧你和我一样,公司里没有设计稿展示的网站,这个时候就要进行手动导入
了。预备手动导入的时候,最重要的就是先了解命名方式
,上方的时候我们提及过一下命名方式
:
backstop_default_BackstopJS_Homepage_0__0_phone.png
,命名分别是测试名字_测试用例ID_选择器_屏幕尺寸
这样的命名方式是默认的,但是也可以通过配置文件修改。
{ // ... fileNameTemplate:'{scenarioIndex}_{scenarioLabel}_{selectorIndex}_{selectorLabel}_{viewportIndex}_{viewportLabel}' , // ... } 复制代码
把从UI设计那里导出的设计稿按照约定命名后,就可以直接进行比对了。
如果这样的话还并不是很清楚规则,所以我们需要了解一下backstop.json
配置文件,来看一下都有哪些配置选项。
{ "id": "backstop_default", // 测试用例id,用于屏幕截图命名。BackstopJS将自动为您生成一个,以避免命名与BackstopJS资源的冲突。 "viewports": [ // 将测试您的DOM的一系列屏幕尺寸对象。根据需要添加任意数量-但至少添加一个 { "label": "phone", // 手机尺寸小 "width": 320, "height": 480 }, { "label": "tablet", // 平板 "width": 1024, "height": 768 } ], "onBeforeScript": "puppet/onBefore.js", // 在执行脚本前的脚本 "onReadyScript": "puppet/onReady.js", // 在执行脚本后的脚本 "scenarios": [ // 测试用例 { "label": "BackstopJS Homepage", // 测试名称 "cookiePath": "backstop_data/engine_scripts/cookies.json", // 设置cookies 对于需要设置cookie的网址可通过此配置文件配置cookie "url": "https://garris.github.io/BackstopJS/", // 必需的。告诉BackstopJS您要测试的端点/文档。这可以是绝对URL,也可以是您当前工作目录的本地URL "referenceUrl": "", // 创建引用时指定不同的状态或环境。 "readyEvent": "", //预定义的字符串记录到控制台来触发屏幕捕获。---实现异步交互 "readySelector": "", // 等到此选择器存在后再继续 ---实现异步交互 "delay": 0, // 延迟 "hideSelectors": [], // 设置为visibility的选择器数组:hidden "removeSelectors": [], // 设置为display的选择器数组:none "hoverSelector": "", // 在截屏之前,将指针移到指定的DOM元素上 "keyPressSelectors": "", // 接受选择器和字符串值数组-模拟多个顺序按键交互。 "clickSelector": "", // 在屏幕截图之前单击指定的DOM元素。 "clickSelectors": "", // *仅限Puppeteer *获取selctors数组 - 模拟多个顺序点击交互。 "postInteractionWait": 0, // 在与hoverSelector或clickSelector交互后等待选择器(可选择接受以ms为单位的等待时间。想法用于单击或悬停元素转换。默认使用onReadyScript) "selectors": [], // 选择需要截图的选择器 "selectorExpansion": true, // 定位元素 "expect": 0, // 跟选择器配合使用,说期望找到的选择器的数量跟配置的数量是否匹配,不匹配的话表示测试失败 "misMatchThreshold": 0.1, // 允许通过测试的不同像素的百分比 "requireSameDimensions": true // 测试必须与参考尺寸相同 } ], "paths": { "bitmaps_reference": "backstop_data/bitmaps_reference", // 存储样板图 "bitmaps_test": "backstop_data/bitmaps_test", // 截图输出路径 "engine_scripts": "backstop_data/engine_scripts", // js配置路径 "html_report": "backstop_data/html_report", // 显示对比图的html "ci_report": "backstop_data/ci_report" }, "report": [ //报告的形式,支持命令行和浏览器两种 "browser" ], "engine": "casper", // 配置引擎属性,slimerjs(Gecko / Mozilla,需要安装),casper,chromy(webkit) "engineOptions": { // 配置引擎属性的默认值 "casperFlags": [ "--engine=slimerjs", "--proxy-type=http", "--proxy=proxyIp:port", "--proxy-auth=user:pass" ] }, "asyncCaptureLimit": 5, // 一次能捕获5个屏幕 "asyncCompareLimit": 50, // 配置测试期间所需的RAM量 "debug": false, // 是否打印测试日志 "debugWindow": false, "resembleOutputOptions": { // 比较差异输出图片的配置 "errorColor": { "red": 255, "green": 0, "blue": 255 }, "errorType": "movement", "transparency": 0.3, "ignoreAntialiasing": true } } 复制代码
配置选项有很多,基本覆盖了大部分的截图需求。
测试命令可以执行指定配置文件,可以从多个配置文件区分环境
backstop test --config=<configFilePathStr> 复制代码
创建一个backstop.config.js
module.exports = { Same object as backstop.json } 复制代码
backstopJs大大减少了UI走查的时间,并且可以应用于自动化,git集成、docker集成,Jenkins/Travis集成都是可以的,功能非常强大。这篇文章起到入个门的作用,后面抽时间单独介绍一下backstopJs在自动化集成上的表现。有一些有趣的backstopJs logo 图片分享给大家