为了账号安全,请及时绑定邮箱和手机立即绑定

聊一聊UI回归测试 | BackstopJs

背景

项目的开发流程中有一个重要的环节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

image.png

创建一个文件夹,我这里文件夹的命名为backstop-article

$ mkdir backstop-article && cd backstop-article
复制代码

进入创建的文件夹后,执行backstopJs init命令,初始化基础项目结构。

$ backstop init
复制代码

初始化后终端显示如下。

image.png

接下来,打开项目,我们看一下目录结构。

image.png

首先分析一下目录结构

 |- 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配置文件
复制代码

ignoreCSP.js

这里稍微科普一下网页安全政策Content Security Policy,缩写 CSP的来历,CSP的诞生是为了防止XSS攻击。

CSP的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置CSP 大大增强了网页的安全性。攻击者即使发现了漏洞,也没法注入脚本,除非还控制了一台列入了白名单的可信主机。

两种方法可以启用CSP。一种是通过HTTP头信息的Content-Security-Policy的字段。

image.png

上述图片捕获自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
复制代码

执行完后会弹出一个页面,如下图所示:

image.png

可以看出最上方有一些筛选操作,可以按照比对成功筛选、失败筛选、模糊筛选,右上角设置按钮可以调整下方对比卡片的展示内容。

接下来我们来看展示内容卡片。卡片左上角展示了当前卡片的基本信息。

在卡片中心部门分为了三大块 reference-参照图片test-测试图片diff-比对图片,但是第一次启动的时候,你所看到的页面和上方的截图是一样的,只有测试图片。下方提示你“当前没有找到参照图片”。因为没有reference参照图片,所以也有没有右侧的diff对比图片

这个时候看一下目录结构。

image.png

目录结构相对最开始的时候发生了一些变化,多出了两个文件夹

  • bitmaps_test // 测试比对结果图片结果,内部以日期+时间文件夹名称区分
  • html_report // 上方弹出界面的HTML文件目录

简单看了一下弹出的页面后,我们回头看一下刚刚执行时候的控制台最下方有一段Error

image.png

报错的为这两句

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参照图片呢,有下面几种方式:

backstop approve

如果当前截图是你认为的参照模板,那么执行这条语句后,当前的测试图片就会存为参照图片。这个模式通常用于批量生成参照图片使用。如果UI设计可以批量导入到另一个网址的时候。不过我们公司没有这种网站,可以考虑搭建一个。

最开始的时候用UI设计库的网址批量生成reference参照图片,然后参照图片生成完毕后,把地址换成开发环境地址,也就是我们还原后的项目地址。这样的话就可以进行UI及开发web之间的比对了。具体操作如下:

执行backstop approve

$ backstop approve
复制代码

执行完毕

image.png

这时候我们再看一下目录结构,会发现多了一个backstop_reference文件夹,创建规则bitmaps_test/<timestamp>/

image.png

backstop_reference文件夹内部则是刚刚测试地址的两张尺寸的图片。那么这个时候项目中就有了参照图片,再执行测试命令的话就可以看到如下完整界面。

image.png

参照图片与测试图片比对通过,没有diff对比图片。因为我们的对照图片就是从测试地址上截取下来的,所以是一模一样的。这个时候如果把测试地址跟换一下的话,就可以看到diff图片了。

image.png

上面的参照图片还是我们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 }
复制代码

END

backstopJs大大减少了UI走查的时间,并且可以应用于自动化,git集成、docker集成,Jenkins/Travis集成都是可以的,功能非常强大。这篇文章起到入个门的作用,后面抽时间单独介绍一下backstopJs在自动化集成上的表现。有一些有趣的backstopJs logo 图片分享给大家

image.png

image.png

backstop_with_puppeteer.jpg

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消