使用 Cypress 进行可视化回归测试:一种务实的方法

每次组件库 Picasso 发布新版本时,都会更新所有的前端应用程序,让绝大部分新功能能与整个平台的设计保持一致。上个月,推出了 Toptal Talent Portal 的 Picasso 更新,这是我们的用户用来找工作和与客户互动的平台。 已知了这个版本将有设计方面的重要更改,并且为了尽量减少意想不到的问题,使用可视化回归测试技术来帮助我们在发布前发现问题是有意义的。

 视觉回归测试并不是一个新概念。 Toptal 的许多其他项目已经在使用它,包括 Picasso 本身。Percy、Happo 和 Chromatic 等工具可用于帮助团队构建健康的视觉回归流水线,最初确实考虑过添加它们。 最后觉得设置过程太耗时,可能会打乱计划。 我们已经为开始迁移的代码冻结设定了日期,距离截止日期只剩下几天了,但别无选择,只能发挥创意。

通过 UI 测试进行视觉回归测试

虽然我们在项目中没有视觉回归测试,但我们确实很好地使用 Cypress 覆盖了的 UI 集成测试。 尽管这不是该工具的主要用途,但 Cypress 在其文档中有一页专门用于可视化测试,另一页列出了所有可用的插件以帮助配置 Cypress 以进行可视化测试。

从Cypress到屏幕截图

在浏览了可用的文档之后,决定尝试一下 cypress-snapshot-plugin。 设置只需要几分钟,完成以后,我们很快意识到不是在追求传统的视觉回归输出。大多数视觉回归工具通过比较快照和检测已知的、可接受的基线与页面或组件的修改版本之间的像素差异来帮助识别不需要的更改。 如果像素差异大于设定的容差阈值,则页面或组件被标记为需要手动检查。 不过,在此版本中,我们知道我们将对大多数 UI 组件进行一些小的更改,因此设置阈值不适用。 即使给定的组件碰巧有 100% 的不同,它在新版本的上下文中可能仍然是正确的。 同样,小到几个像素的偏差可能意味着组件当前不适合生产。

 那时,两件截然不同的事情变得清晰起来:注意到像素差异无助于识别问题,而对组件进行并排比较正是我们所需要的。 我们将快照插件放在一边,开始使用组件在应用 Picasso 更新之前和之后创建一个图像集合。 这样,就可以快速扫描所有更改,以确定新版本是否仍然符合网站的需求和图书馆的标准。新的计划是截取一个组件的屏幕截图,将其存储在本地,然后在具有更新的 Picasso 版本的分支中截取相同组件的新屏幕截图,然后将它们合并为一个图像。 最终,这种新方法与我们开始的方法并没有太大不同,但它在实施阶段为我们提供了更大的灵活性,因为不再需要导入插件并使用其新命令。

利用 API 比较图像

有了明确的目标,是时候看看 Cypress 如何帮助我们获得所需的屏幕截图了。 如前所述,我们进行了大量的 UI 测试,涵盖了人才门户的大部分内容,因此为了尽可能多地收集关键组件,我们决定在每次交互后截取各个元素的屏幕截图。另一种方法是在测试期间的关键时刻截取整个页面的屏幕截图,但我们认为这些图像太难比较了。 此外,此类比较更容易出现人为错误,例如遗漏页脚已更改的信息。第三种选择是通过每一个测试用例来决定要捕获什么,但这会花费更多时间,因此坚持使用页面上的所有元素似乎是一种实际的妥协。

我们使用Cypress的 API 来生成图像。 cy.screenshot() 命令可以开箱即用地创建单独的组件图像,After Screenshot API 允许重命名文件、更改目录以及区分视觉回归运行和标准回归运行。 通过结合这两者,我们创建了不影响功能测试的运行,并能够将图像存储在适当的文件夹中。首先,我们扩展了插件目录中的 index.js 文件以支持两种新的运行类型(基线和比较)。 然后,根据运行类型设置图像的路径:

 
  1. // plugins/index.js

  2. const fs = require('fs')

  3. const path = require('path')

  4. module.exports = (on, config) => {

  5. // Adding these values to your config object allows you to access them in your tests.

  6. config.env.baseline = process.env.BASELINE || false

  7. config.env.comparison = process.env.COMPARISON || false

  8. on('after:screenshot', details => {

  9. // We only want to modify the behavior of baseline and comparison runs.

  10. if (config.env.baseline || config.env.comparison) {

  11. // We keep track of the file name and number to make sure they are saved in the proper order and in their relevant folders.

  12. // An alternative would have been to look up the folder for the latest image, but this was the simpler approach.

  13. let lastScreenshotFile = ''

  14. let lastScreenshotNumber = 0

  15. // We append the proper suffix number to the image, create the folder, and move the file.

  16. const createDirAndRename = filePath => {

  17. if (lastScreenshotFile === filePath) {

  18. lastScreenshotNumber++

  19. } else {

  20. lastScreenshotNumber = 0

  21. }

  22. lastScreenshotFile = filePath

  23. const newPath = filePath.replace(

  24. '.png',

  25. ` #${lastScreenshotNumber}.png`

  26. )

  27. return new Promise((resolve, reject) => {

  28. fs.mkdir(path.dirname(newPath), { recursive: true }, mkdirErr => {

  29. if (mkdirErr) {

  30. return reject(mkdirErr)

  31. }

  32. fs.rename(details.path, newPath, renameErr => {

  33. if (renameErr) {

  34. return reject(renameErr)

  35. }

  36. resolve({ path: newPath })

  37. })

  38. })

  39. })

  40. }

  41. const screenshotPath = `visualComparison/${config.env.baseline ? 'baseline' : 'comparison'}`

  42. return createDirAndRename(details.path

  43. .replace('cypress/integration', screenshotPath)

  44. .replace('All Specs', screenshotPath)

  45. )

  46. }

  47. })

  48. return config

  49. }

然后通过将相应的环境变量添加到项目的 package.json 中的 Cypress 调用来调用每个运行:

 
  1. "scripts": {

  2. "cypress:baseline": "BASELINE=true yarn cypress:open",

  3. "cypress:comparison": "COMPARISON=true yarn cypress:open"

  4. }

运行新命令后,可以看到运行期间截取的所有屏幕截图都已移动到相应的文件夹中。

接下来,尝试覆盖 cy.get(),这是 Cypress 返回 DOM 元素的主要命令,并对调用的任何元素及其默认实现进行截图。 不幸的是,cy.get() 是一个很难更改的命令,因为在其自己的定义中调用原始命令会导致无限循环。 解决此限制的建议方法是创建一个单独的自定义命令,然后让该新命令在找到元素后截取屏幕截图:

  1. Cypress.Commands.add("getAndScreenshot", (selector, options) => {

  2. // Note: You might need to tweak the command when getting multiple elements.

  3. return cy.get(selector).screenshot()

  4. });

  5. it("get overwrite", () => {

  6. cy.visit("https://example.cypress.io/commands/actions");

  7. cy.getAndScreenshot(".action-email")

  8. })

但是,与页面上的元素进行交互的调用已经包含在内部 getElement() 函数中。 所以我们所要做的就是确保在调用包装器时截取屏幕截图。

通过视觉回归测试得到的结果

一旦我们有了屏幕截图,剩下要做的就是合并它们。 为此,使用 Canvas 创建了一个简单的节点脚本。 最后,脚本能够生成 618 张比较图像! 通过打开人才门户很容易发现一些差异,但有些问题并不那么明显。 

图 4. 不遵循新毕加索指南的示例; 预计会有所不同,但新版本应该有红色背景和白色文本

图 5. 略有损坏的组件布局示例 

为 UI 测试增加价值

首先,添加的视觉回归测试被证明是有用的,并且发现了一些如果没有它们我们可能会错过的问题。 尽管和预计组件会有所不同,但了解实际更改的内容有助于缩小问题案例的范围。 所以,如果你的项目有一个接口,但还没有执行这些测试,那就开始吧!

 这里的第二个教训,也许是更重要的一个教训,是我们再次被提醒完美是好的敌人。 如果我们因为没有事先设置而排除了为此版本运行视觉回归测试的可能性,那么可能会在迁移过程中错过一些错误。 相反,我们商定了一个计划,虽然不理想,但执行起来很快,朝着它努力,就可以得到回报。

总结:

感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

 

          视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方进群即可自行领取。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/730866.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软考70-上午题-【面向对象技术2-UML】-UML中的图1

一、图的定义 图是一组元素的图形表示,大多数情况下把图画成顶点、弧的联通图。 顶点:代表事物; 弧:代表关系。 可以从不同的角度画图,UML提供了13种图:(只看9种) 类图&#xff…

数据结构实现

目录 一、线性表中顺序表的实现: 二、线性表的链式存储(链表-带头节点) 三、习题练习: 四、栈(stack) 五、循环队列 1.数组形式: 2.链表形式: 3.习题练习 六、二叉树 1.…

226. 翻转二叉树

代码实现: 方法1:先序遍历 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/// 交换左右子树 void swap(struct TreeNode *root) {struct TreeNode *l root…

Linux grep

文章目录 1. 基本用法2.字符转义3.二进制文件查找4.打印目标字段的附近行4. 多条件过滤5. 目录中过滤——用于在文件夹中筛选/排除指定后缀文件6.反向过滤——用于筛选7.只输出匹配内容——用于统计8. 筛选出包含字段的文件9.正则匹配10.管道和grep11.grep和wc/uniq/sort的合用…

B端系统优化,可不是换个颜色和图标,看看与大厂系统的差距。

Hi,我是贝格前端工场,优化升级各类管理系统的界面和体验,是我们核心业务之一,欢迎老铁们评论点赞互动,有需求可以私信我们 一、不要被流于表面的需求描述迷惑。 很多人找我们优化系统界面,对需求总是轻描淡…

小白跟做江科大51单片机之AD/DA

1.看原理图找接口 2.看时序图编写读取数据代码 XPT2046.c代码 #include <REGX52.H> //引脚定义 sbit XPY2046_DINP3^4; sbit XPY2046_CSP3^5; sbit XPY2046_DCLKP3^6; sbit XPY2046_DOUTP3^7; unsigned int XPT2046_ReadAD(unsigned char Command) { unsigned char …

sqllab 11-22

11.有回显&#xff0c;单引号 首先判断是字符型还是数字型 通过order by 来获取字段数 方便后续union联合 注意这里mime表明了内容要进行url编码&#xff0c;测试3报错&#xff0c;2正常&#xff0c;所以有2列。 还需要判断显示位&#xff0c;因为只有显示位的数据才能被爆出…

论文笔记:Efficient Bootstrapping for Confidential Transactions

EcoBoost: Efficient Bootstrapping for Confidential Transactions 设计了一种被称为EcoBoost的新方法&#xff0c;以提高支持机密交易的区块链的引导效率。具体来说&#xff0c;利用随机抽样来验证高概率保密交易的正确性。因此&#xff0c;与事务数量相比**&#xff0c;验证…

【FPGA】DDR3学习笔记(一)丨SDRAM原理详解

本篇文章包含的内容 一、DDR3简介1.1 DDR3 SDRAM概述1.2 SDRAM的基础结构 二、 SDRAM操作时序2.1 SDRAM操作指令2.2 模式寄存器&#xff08;LOAD MODE REGISTER&#xff09;2.3 SDRAM操作时序示例2.3.1 SDRAM初始化时序2.3.2 突发读时序2.3.3 随机读时序2.3.4 突发写时序2.3.5 …

Python学习笔记-Flask实现简单的抽奖程序(增加图片显示)

1.创建static文件夹,存放图片文件 2.hero列表数据更改为要抽奖的图片名 3.html中可以编写python语句,遍历hero列表内容渲染到表格中 4.在点击随机抽取后,可以获得名称,然后使用img标签,将获取的名称拼接到路径中 3.初始页面,访问127.0.0.1:5000/index 4.点击随机抽取后 5.py…

【深入理解设计模式】享元设计模式

享元设计模式 概述 享元设计模式&#xff08;Flyweight Design Pattern&#xff09;是一种用于性能优化的设计模式&#xff0c;它通过共享尽可能多的相似对象来减少对象的创建&#xff0c;从而降低内存使用和提高性能。享元模式的核心思想是将对象的共享部分提取出来&#xff…

实用干货:分享4个冷门但非常实用的HTML属性

大家好&#xff0c;我是大澈&#xff01; 本文约1100字&#xff0c;整篇阅读大约需要2分钟。 关注微信公众号&#xff1a;“程序员大澈”&#xff0c;免费加入问答群&#xff0c;一起交流技术难题与未来&#xff01; 现在关注公众号&#xff0c;免费送你 ”前后端入行大礼包…

TensorRT是什么,有什么作用,如何使用

TensorRT 是由 NVIDIA 提供的一个高性能深度学习推理&#xff08;inference&#xff09;引擎。它专为生产环境中的部署而设计&#xff0c;用于提高在 NVIDIA GPU 上运行的深度学习模型的推理速度和效率。以下是关于 TensorRT 的详细介绍&#xff1a; TensorRT 是 NVIDIA 推出的…

freeRTOS20240308

1.总结任务的调度算法&#xff0c;把实现代码再写一下 2.总结任务的状态以及是怎么样进行转换的

Java集合面试题(day 02)

&#x1f4d1;前言 本文主要是【JAVA】——Java集合面试题的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&am…

容器: string

引言: 为什么要有string类型, 就使用字符数组表示字符串不行吗? 原因: 使用字符数组描述文本信息, 无法确定开多大空间, 开多了浪费,开少了不够用使用string封装: 扩容机制:减少了空间的浪费各种接口:方便修改等操作 string的使用 容量相关 size:获取字符个数,不包含\0 (C语言…

从huggingface下载模型像本地加载但是UnicodeDecodeError

我自己是在Linux下出现了这个问题 原文&#xff1a;https://github.com/huggingface/transformers/issues/13674 The path for the AutoModel should be to a directory pointing to a pytorch_model.bin and to a config.json. Since you’re pointing to the .bin file dire…

无限debugger的几种处理方式

不少网站会在代码中加入‘debugger’&#xff0c;使你F12时一直卡在debugger&#xff0c;这种措施会让新手朋友束手无策。 js中创建debugger的方式有很多&#xff0c;基础的形式有&#xff1a; ①直接创建debugger debugger; ②通过eval创建debugger&#xff08;在虚拟机中…

安全防御-第七次

在FW5和FW6之间建立一条IPSEC通道保证10.0.2.0/24网段可以正常访问到192.168.1.0/24 NAT&#xff1a; 安全策略&#xff1a; NAT: 安全策略&#xff1a; 修改服务器映射&#xff1a; 配置IPSEC&#xff1a;

物联网的商业模式洞察

大约在十年前&#xff08;2014年11月&#xff09;&#xff0c;全球知名管理思想家、哈佛商学院教授迈克尔波特与PTC前首席执行官吉姆赫普尔曼&#xff0c;在《哈佛商业评论》上联合撰写了一篇备受赞誉的文章&#xff0c;题为《智能互联产品如何改变竞争》。在这篇文章中&#x…