写点什么

视觉感知测试

  • 2014-06-20
  • 本文字数:8050 字

    阅读完需:约 26 分钟

随着互联网第二春的到来以及 Web2.0 的盛行,Web 应用程序开发已经成为了当前软件开发的主力军。现在无论是企业级应用,社交应用还是移动应用,Web 已经成为标准配置,而且很多企业正在逐步的将自己的企业级本地应用进行互联网 Web 化。但是 Web 的界面布局测试,多浏览器测试,CSS/JavsScript 的重构等都成为界面测试的痛中之痛,特别是大型 Web 应用的回归测试量太大,从而导致回归测试很多时候根本无法完成,所以很少会有团队能完成全方位的界面布局回归测试,特别是对于使用 Agile 流程开发的团队就更加困难。

而且现在大家对用户体验以及持续部署越来越重视,导致 Web 应用程序的界面开发和测试难上加难。

首先来看看 Web 界面开发和测试为什么如此困难。

1,Web 界面布局回归测试

对于 Web 网页界面布局测试一般都是由人工手动对比设计图和产品界面。而人工对比测试存在两个问题:a, 速度慢;b, 人的不确定性。对于拥有大量复杂界面的企业级 Web 应用,界面布局的回归测试的数量巨大,再加上这两个问题,导致这类应用的界面布局回归测试时间很长,成本很高,所以很多基于 Agile 项目基本不可能在迭代周期内高质量的完成其界面回归测试。对于每天做一次回归,那更是不可能完成的任务。

(下面有一个游戏“大家来找茬”,请读者用心找找有多少处不同,并记录一下用了多少时间。答案在附 1 图中)

图 1,大家来找茬

2,CSS/JavaScript 代码重构

现在 Web 前端越来越复杂,所以代码量也急速增加,导致前端开发像后端开发一样开始使用基于 Library, Module 和 Pattern 的开发方式。从而产生了一个问题:当有公共代码被修改和重构之后,如何快速发现界面的 side effect?

由于 CSS 和控制界面的 JavaScript 代码被重构之后,只能通过人眼手动检测其正确性,导致开发和测试人员很难在有限的时间找到所有被修改的代码影响到的界面进行检查。最后很可能会有一些 side effect 在开发和测试阶段都不被发现而进入产品环境。

3,多浏览器

Web 应用其最大的优势就是其可以跨平台跨浏览器,使用者可以在不同的操作系统中使用不同的浏览器访问并使用 Web 应用。但是这个优势也带来了很大的问题:需要做大量的浏览器兼容性测试。而被测浏览器的数量越多(现在的主流浏览器包括 IE, Chrome, Firefox, Safari 等,并且每种浏览器还有很多种版本),测试数量和时间也会成倍增长。这个痛也导致很多大型 Web 应用基本上很难在限定时间内完成大部分主流浏览器的兼容性测试。如果一定要做,那么也需要付出巨大的成本,比如添加更多的测试人员。

4,响应式设计 (Responsive Web Design) 测试

由于移动设备的普及,导致大量的用户使用手机或者平板使用 Web 应用。由于移动设备拥有各种各样的分辨率,因此设计人员也开始考虑针对不同的分辨率设计应用界面,响应式设计 (Responsive Web Design) 也孕育而生。但是响应式设计很难测试,基本上只能靠手工进行,而且还需要准备各种分辨率的设备或者各种分辨率的浏览器。需要测试的分辨率越多,测试的时间越长,成本就也越高。

下面有两张真实网页的截图,其中有很多不同之处,读者可以尝试再找一下有多少。答案在附 2 图中。

图 2,网页 1

图 3,网页 2

什么是视觉感知测试 -Perceptual Testing

对于界面布局,传统的测试都是由人工对比设计图和产品界面。当界面有修改之后,再由人通过肉眼去检查修改 (包括正确的和错误的修改),这样即费时而且测试结果又不稳定,因为人是有情绪的。但是我们认为如果一个界面通过第一次的人工验证并发布之后,它就是一个正确的标准界面,并且是包含了人工测试价值的资产。当下一次测试的时候,这部分价值就应该被保留并重用起来,用于减少新的一次测试的时间,从而实现界面的快速回归测试。

为了解决上面提到的各种问题,视觉感知测试孕育而生。它使用传统的对图片进行二进制比较的办法,结合敏捷迭代开发的理念,产生的一种针对界面布局的自动化测试方法。

视觉感知测试包含以下几个主要的测试步骤:

  1. 对于产品版本进行截图(产品线上环境或者类产品环境) 首先人工完成第一个软件版本的测试并部署上线,在第二个版本需要进行测试的时候首先对第一个版本的所有界面进行截图。
  2. 对于新的发行版进行截图(比如 staging 环境) 然后对第二个需要进行测试的版本的所有界面也进行截图。
  3. 配对 URL(忽略 hostname) 通过配对 URL,对所有的截图按照相同的 URL 进行分组。当然有时候会出现新的界面,有时候老的界面会被删除。对于新的界面就需要人工进行首次验证测试 。
  4. 像素级别的图形比较 对于分组之后的截图进行像素级别的比较并生产差别图。有时候为了降噪,可以只对局部关心的组件进行比较。
  5. 人工查看所有不同 最后通过人工审查差别图报告完成测试。

视觉感知测试的一些实例

1,CSS 的改变

对于开发人员,CSS 改变之后的 side effect 是最头痛的事情,下面展示了当 CSS 改版之后页面的变化

图 4,CSS 的改变

2,内容的改变

对于测试人员,大量复杂页面的微小修改很难发现的,下面展示了如果使用视觉比较找到差异。

图 5,内容的改变 1

图 6,内容的改变 2

3,事件处理的改变

对于测试人员,有些界面需要鼠标点击或者悬停才能展现出来。而对于这样的界面的测试就必须人工来做。下面展示了一个选择框在鼠标点击之后在两个版本之间产生的差异。

图 7,事件处理的改变

4,响应式设计

对于开发和测试人员,如果要测试响应式设计就必须使用不同分辨率的设备,模拟器或者调整浏览器到各种分辨率,这将是一个费时费力费钱的工作。下面展示了如果视觉比较如果检查响应式设计。

图 8,响应式设计

视觉对比 - UI 自动化 end-to-end 测试的最后一公里

持续交付中的视觉感知测试

下图为传统的持续交付流程:

图 9,没有视觉感知的持续交付

下图为加入了视觉感知测试的持续交付流程,其中主要的区别就是部署之前要并行与其他自动化测试做一次视觉感知测试。

图 10,包含视觉感知测试的持续交付

下图为实施了视觉感知测试之后对于界面回归测试的时间示意图

图 11,界面回归测试时间示意图

三个视觉感知测试工具

1,Mogotest

  • 来自 Mogotest,基于“云”
  • Restful API 开发测试
  • 支持多浏览器
  • 不支持本地化
  • 商用
  • http://mogotest.com/

Mogotest 是一个商用的产品,它提供一个“云”测试平台,可以让用户在其平台上使用各种不同的浏览器访问被测试页面,并进行对比。主要目的是测试不同浏览器之间的兼容性,不能测试动态页面等。

2,DPXDT

Dpxdt 是基于 Python 和 PhantomJS 开发的一个 Web Service 系统,其中 PhantomJS 可以理解为一个没有界面的浏览器。用户使用其提供的 RESTFul API 可以十分方便的对比两个页面,而且它还提供一个功能十分强大的报表系统。对于全部是静态页面的 Web 系统来说非常适用,不过对于需要手动导航,比如需要进行输入,点击或者鼠标悬停等操作之后才能进行检测的界面,它默认并不支持,需要对其本身进行修改才可以。不过它还提供了一个方式可以把他很方便的部署到 GWS 上。

3,Viff

  • 来自 ThoughtWorks,基于 Javascript/node
  • 支持多浏览器(Selenium WebDriver)
  • JavaScript/DSL 开发测试
  • 支持人工认证报表系统
  • 开源,免费
  • 支持嵌入式测试:智能电视,手机等
  • https://github.com/winsonwq/viff

Viff 是基于 NodeJS 和 Selenium 开发的一个本地工具。通过编写 JavaScript 代码来调用 Selenium API, 并在真实的浏览器中进行截图比较。所以它比较适合动态的 Web 系统,因为可以编写代码模拟用户输入和点击操作。由于它底层使用的是 Selenium 作为驱动,所以他支持多种浏览器,比如 IE,Chrome,Firefox 等。由于最新的 Selenium 加入了对 Android 和 iOS 的支持,因而 Viff 也能够支持 Android 和 iOS 上的浏览器测试。

如果对你来说搭建多浏览器环境比较困难,比如需要同时测试 IE8,IE9,IE10 等,可以选择 BrowserStack。BrowserStack 是一个商业产品,他同时通过 Web 界面和 API 接口提供多浏览器环境给客户进行 Web 测试,Viff 可以使用其 API 进行进行多浏览器截图。对于 Viff,由于编写 JavaScript 代码也需要一定的门槛,所以对于没有代码能力的使用者在测试静态网页的时候应该选择 Dpxdt,但是如果你有一定的代码能力,并且希望能在当前的功能测试里面加上视觉感知测试或者希望对局部的界面进行测试,建议选用 Viff。现在 Viff 正在开发 Web Service 功能,这样以后就可以作为一个 Service 进行部署和使用。

还有其他的视觉感知测试工具,这里就不一一熬述了。在 VIFF 的官网上有一张多个工具的比较图,有兴趣的读者可以参考一下: http://twers.github.io/Viff-Service/

VIFF 演示

1,安装

VIFF 的安装步骤请参考其项目上的说明文档 https://github.com/winsonwq/viff

为了帮助大家理解和学习 VIFF,我们还开发了一系列的 Examples 和 Demos,请参见 https://github.com/winsonwq/viff-examples ,以下所有代码全部来自这个项目。

2, 安装需要被测试的演示网站

下载 https://github.com/winsonwq/viff-examples 上的代码,模拟产品版本的站点在 viff-examples/example/prod 里面,模拟需要测试的站点在 viff-examples/example/build 里面。由于演示网站都是静态代码,所以用任意一个 HTTP Sever 进行部署都可以,比如我使用 Nginx 将其部署在本地的 8000 端口上。部署成功之后,通过 http://localhost:8000/example/build http://localhost:8000/example/prod 就可以访问到两个测试演示站点。

3, 使用 VIFF 的 Main API 进行测试

对于一个全新的项目,直接使用 VIFF 的 Main API 编写测试代码,如下:

复制代码
'use strict'
var config = module.exports = {
seleniumHost: 'http://localhost:4444/wd/hub',
browsers: ['firefox'],
envHosts: {
build: 'http://localhost:8000/example/build',
prod: 'http://localhost:8000/example/prod'
},
paths: [],
reportFormat: 'file',
test: function test (description, caseConfig) {
var c = {};
c[description] = caseConfig;
this.paths.push(c);
}
};
config.test('Home Page', ['/github.html', function (browser) {
return browser.waitForElementByCssSelector('.repo-list-item', browser.isDisplayed());
}]);
config.test('Search Result', ['/github.html', function (browser) {
return browser
.waitForElementByCssSelector('.repo-list-item', browser.isDisplayed())
.elementByCssSelector('[type="search"]').type('commander.js')
.sleep(1000);
}]);
config.test('Open Readme file', ['/github.html', function (browser) {
return browser
.waitForElementByCssSelector('.repo-list-item', browser.isDisplayed())
.elementByCssSelector('.repo-list-item:nth-child(2)').click()
.waitForElementByCssSelector('.repo-readme', browser.isDisplayed());
}])

测试结果报表如下:

1,首先测试主页,由于没有任何改动,所以测试结果是绿色,表示产品环境和测试环境没有任何改变。

图 12,演示结果报表 1

2,然后在搜索输入框中输入 commander.js,结果发现产品版本上的 show 1 repository 在测试版本上变成了 showing 1 repositories,所以测试结果是红色。然后通过报表可以在立即发现改变,然后在进行人工审核其修改的正确性。这里明显是测试版本出现了错误,然后针对这个错误就可以上报一个 bug 了。

图 13,演示结果报表 2

3,清空输入框,然后在主页中点击 co,然后测试结果还是红色。认真仔细的查看才发现 generator 在测试版本中被改成了 generators。在如此多的内容中找到一个字母 s 的改变是非常困难的,但是通过 VIFF 的测试报告,又一次快速的轻松地发现了改变。

图 14,演示结果报表 3

4,使用 VIFF 的 Client API 进行测试

对于一个已经有 Functional Testing 的项目,可以不需要重新开发 VIFF 测试代码,只需要在功能测试代码中调用其 Client API,同样可以完成视觉感知测试。由于当前 VIFF 只开发了 JavaScript 版本的 Client API,所以下面的例子使用的是基于 JavaScript 开发的 Functional Testing。以后 VIFF 会初步提供基于其他语言的 Client API,比如 Java,Python 和 Ruby 等。

(代码说明,其中两行绿色代码表示分别对产品环境和测试环境进行 Functional Testing;其中所有的蓝色代码是 Functional Testing 的代码;其中所有红色部分代码是 VIFF 的 Client API)

复制代码
var wd = require('wd');
var chai = require("chai");
var chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);
chai.should();
var ViffClient = require('viff-client');
chaiAsPromised.transferPromiseness = wd.transferPromiseness;
describe('Github Page Test', function() {
this.timeout(100000);
var browser, buildScreenshot, prodScreenshot, buildClient, prodClient;
before(function (done) {
browser = wd.promiseChainRemote('http://localhost:4444/wd/hub');
browser.init({ browserName: 'firefox' }).nodeify(done);
buildClient = new ViffClient('http://localhost:3000', {
name: 'build',
host: 'http://localhost:8000/example/build/github.html',
capabilities: 'firefox'
});
buildScreenshot = prepareTakeScreenshot(browser, buildClient);
prodClient = new ViffClient('http://localhost:3000', {
name: 'prod',
host: 'http://localhost:8000/example/prod/github.html',
capabilities: 'firefox'
});
prodScreenshot = prepareTakeScreenshot(browser, prodClient);
});
after(function (done) {
buildClient.generateReport(function () {
browser.quit().nodeify(done);
});
});
<span color="#00ff00">describe('in build environment', function() {</span>
beforeEach(function(done) {
browser
.get("http://localhost:8000/example/build/github.html")
.waitForElementByCssSelector('.repo-list-item', browser.isDisplayed())
.nodeify(done);
});
<span color="#0000ff">it('could go to Home Page', function(done) {</span>
browser.title().should.become("TJ Holowaychuk's Github Repositories")
.then(function () {
<span color="#ff0000">buildScreenshot({ 'Home Page': ['/github.html'] }, 'screenshots/homepage.png', done);</span>
});
});
<span color="#0000ff">it('should filter repo as per keyword', function(done) {</span>
browser
.elementByCssSelector('[type="search"]').type('commander.js')
.sleep(1000)
.elementsByCssSelector('.repo-list-item')
.then(function (elements) {
elements.length.should.eql(1);
<span color="#ff0000"> buildScreenshot({ 'Search Result': ['/github.html'] }, 'screenshots/filter.png', done);</span>
});
});
<span color="#0000ff">it('should open readme file', function(done) {</span>
browser
.elementByCssSelector('.repo-list-item:nth-child(2)').click()
.waitForElementByCssSelector('.repo-readme', browser.isDisplayed())
.elementByCssSelector('.repo-readme').text().should.eventually.contain("# Co")
.then(function () {
<span color="#ff0000">buildScreenshot({ 'Should open readme file': ['/github.html'] }, 'screenshots/github.png', done);</span>
});
});
});
<span color="#00ff00">describe('in prod environment', function() { </span> beforeEach(function(done) {
browser
.get("http://localhost:8000/example/prod/github.html")
.waitForElementByCssSelector('.repo-list-item', browser.isDisplayed())
.nodeify(done);
});
<span color="#0000ff">it('could go to Home Page', function(done) {</span>
browser.title().should.become("TJ Holowaychuk's Github Repositories").then(function () {
<span color="#ff0000">prodScreenshot({ 'Home Page': ['/github.html'] }, 'screenshots/homepage.png', done); </span><span color="#000000"> });</span>
});
<span color="#0000ff"> it('should filter repo as per keyword', function(done) {</span>
browser.elementByCssSelector('[type="search"]').type('commander.js')
.sleep(1000)
.elementsByCssSelector('.repo-list-item')
.then(function (elements) {
elements.length.should.eql(1);
<span color="#ff0000">prodScreenshot({ 'Search Result': ['/github.html'] }, 'screenshots/filter.png', done);</span>
});
});
<span color="#0000ff">it('should open readme file', function(done) {</span>
browser
.elementByCssSelector('.repo-list-item:nth-child(2)').click()
.waitForElementByCssSelector('.repo-readme', browser.isDisplayed())
.elementByCssSelector('.repo-readme').text().should.eventually.contain("# Co")
.then(function () {
<span color="#ff0000">prodScreenshot({ 'Should open readme file': ['/github.html'] }, 'screenshots/github.png', done); </span> });
});
});
});
function prepareTakeScreenshot(browser, viffClient) {
return function (url, imagePath, callback) {
browser
.saveScreenshot(imagePath)
.then(function () {
viffClient.post(url, imagePath, callback);
});
};
}

测试结果报表同第 2 步一样,见图 12,图 13 和图 14

5,使用 VIFF 对移动 Web 进行测试

由于 VIFF 使用的 Selenium,而 Selenium 本身支持移动 Web 支持,所以 VIFF 与生俱来就包含此功能。比如现在要对 iPhone 进行测试,只需要将第 2 步 Demo 代码中的 firefox 改成 iPhone 就可以了。(如果对 Selenium 测试 iPhone 的测试环境有疑问,请参见 Selenium 官方文档和 Selenium iPhoneDriver: https://code.google.com/p/selenium/wiki/IPhoneDriver)

视觉感知测试的几个主要使用场景

  1. Web 应用界面的回归测试
  2. 智能嵌入式应用界面的回归测试 - 如智能电视应用,智能手机应用
  3. 大数据呈现的回归测试

总结

视觉感知测试是近几年为了解决大量繁重的人工界面回归测试才出现的一种自动化测试方法。它不仅能帮助测试人员进行界面回归测试,而且还能帮助开发人员在重构或修改公共 UI 代码的时候快速进行 side effect 检查,从而大大减少了测试的时间,并且使得对大量的界面进行回归测试成为了现实,最终增加了软件的质量,特别是最大可能的保证了软件的用户体验。希望在不久的将来,越来越多的复杂界面系统将会使用视觉感知测试来快速完成其全面的回归测试,从而实现真正的高质量的快速持续交付。

附 1:

图 15,大家来找茬结果

图 16,网页对比结果

作者简介

刘冉,现任 ThoughtWorks 高级软件质量咨询师, 超过 10 年软件开发和测试工作工作经验。最熟悉的领域是嵌入式系统开发、Linux 系统开发、各种脚本、各种测试工具、各种自动化测试系统开发、以及 Agile 中的 QA。其中对于服务器性能测试,Web 功能测试,以及测试分层一体化解决方案有较深的理解。现在关注于全方位自动化 QA 的工作,以及对于 Agile 流程中怎么实现统一的流程、故事、功能、测试和文档管理,以及质量控制度量。


感谢张逸对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2014-06-20 00:178800

评论

发布
暂无评论
发现更多内容

软件测试 | 测试开发 | app自动化测试(Android)-- 特殊控件 T识别oast

测吧(北京)科技有限公司

自动化测试 Android;

设计模式的艺术 第二十二章观察者设计模式练习(开发一款实时在线股票软件。该软件需要提供如下功能:当股票购买者所购买的某只股票价格变化幅度达到5%时,系统将自动发送通知(包括新价格)给购买该股票的所有股民。试使用观察者模式设计并实现该系统)

代廉洁

【荣耀开发者服务平台—百亿曝光扶持等你来】智慧服务内容接口卡片接入指南

荣耀开发者服务平台

手机 激励 卡片服务 厂商 honor

软件测试 | 测试开发 | 一文搞懂测试左移和测试右移的 Why-How-What

测吧(北京)科技有限公司

测试 安全测试

软件测试 | 测试开发 | app自动化测试(Android)--显式等待机制

测吧(北京)科技有限公司

测试

Serverless 架构下的 AI 应用开发:入门、实战与性能优化

阿里巴巴云原生

阿里云 Serverless 云原生

版本管理 | 如何解决SVN的合并冲突与分支问题?

龙智—DevSecOps解决方案

svn 版本管理

2022最新BATJ等一线互联网大厂秋招面试题汇总,速刷

程序知音

Java 程序员面试 后端技术 Java面试题 Java面试八股文

深圳web前端技术培训学习费用

小谷哥

2022年8月国产数据库大事记-墨天轮

墨天轮

数据库 opengauss 国产数据库 达梦 polarDB

牛客“基础-中级-高级”Java程序员面试八股文集结,熬夜挑灯刷

程序知音

Java java面试 后端技术 Java面试八股文 Java 面试题

测试管理 | 龙智获得Xray专家认证

龙智—DevSecOps解决方案

Jira插件

区块链NFT网站开发:NFT数字藏品网站开发

开源直播系统源码

NFT 数字藏品 数字藏品系统

LED屏幕有色差要怎么办?

Dylan

LED显示屏 户外LED显示屏 led显示屏厂家

硅谷名企、国内大厂是如何度量研发效能的?|ONES 研发管理大师课

万事ONES

leetcode 104. Maximum Depth of Binary Tree 二叉树的最大深度(简单)

okokabcd

LeetCode 算法与数据结构

CI/CD | 大型企业与开发团队如何进行持续集成与持续发布

龙智—DevSecOps解决方案

持续集成 CI/CD 持续发布

国产操作系统应用小程序化:夯实技术底座,促进生态发展

Speedoooo

小程序 国产操作系统 小程序容器

北京哪家WEB前端培训机构比较不错

小谷哥

软件测试 | 测试开发 | 接口管理工具YApi怎么用?颜值高、易管理、超好用

测吧(北京)科技有限公司

测试 Mock

代码质量与安全 | 实践“边写边清理”,您需要做好这两件事:质量配置文件和质量门

龙智—DevSecOps解决方案

代码质量 代码安全 静态代码安全

软件测试 | 测试开发 | 基于Requests与mitmproxy打造迷你接口测试框架

测吧(北京)科技有限公司

测试 Request

最后 3 天|报名参加 OpenYurt+EdgeX 挑战赛 ,冲击最高 5 万元奖励!

阿里巴巴云原生

阿里云 云原生 openyurt EdgeX

在Java培训机构中怎么学习?

小谷哥

软件测试 | 测试开发 | RPC接口测试技术-Tcp 协议的接口测试

测吧(北京)科技有限公司

软件测试 | 测试开发 | 文未有福利 | 接口自动化你不懂?听HttpRunner的作者怎么说

测吧(北京)科技有限公司

测试 接口调试

我用 极狐 Gitlab issue 来点菜 #JIHULAB 101

朱亚光

JIHULAB 101

软件测试 | 测试开发 | 抓包分析 TCP 协议

测吧(北京)科技有限公司

TCP 抓包分析

GOPS现场 | 对话龙智技术顾问,分享DevOps观察与心得

龙智—DevSecOps解决方案

运维 DevOps工具链

设备健康管理在石化行业的探索与实践

PreMaint

预测性维护 设备健康管理

数据变更白屏化利器-推送轨迹上线

阿里巴巴云原生

zookeeper 阿里云 开源 微服务 云原生

视觉感知测试_软件工程_刘冉_InfoQ精选文章