自动化测试发展
提起自动化大家一定不会陌生,工业自动化、办公自动化、测试自动化,毕竟解放双手是从古人那里就开始传承下来的传统。比如古人的造纸术、活字印刷术,以及各种农业生产工具,无论古人还是当下的我们,都在不断寻找各种工具来解放双手。
什么是自动化测试
百度百科会这样告诉你
一般是指软件测试的自动化,软件测试就是在预设条件下运行系统或应用程序,评估运行结果,预先条件应包括正常条件和异常条件。
简单来理解,自动化就是尽可能的模拟人为的操作来对应用程序进行检测,比如你造了一个计算机,那么自动化测试就将会试一试开机是否好用,程序是否可以运行。
前端自动化测试
关于前端自动化测试我们分为三部分:效果(UI界面)测试、交互测试、单元测试。
- 效果(UI界面)测试
效果测试是指当前的页面效果是否与效果图一致,可以通过计算二者的差异度,进而得出相似度,显然如果相差70%,那必然不会合格,至于多少可以通过,完全可以自行来指定。
- 交互测试
前端和用户有着频繁的交互,比如用户鼠标单击与移动,键盘的按下与抬起等等,正是凭借着流畅且高频的交互,才能够给用户更好的体验。而交互测试要做的,就是模拟用户的操作来点击或者移动等等。进而判断交互后的结果是否符合网站开发者的预期。
- 单元测试
单元测试在各个语言的测试框架中都是必不可少的一部分,单元测试是指程序中最小可测试单元,显然,你的项目代码耦合度越低,颗粒度越小,那么可测试的单元就越多,就跟答题一样,验算做的多,每个步骤的验算越准确,是不是正确性就越高呢,单元测试就是这个道理。
效果(UI界面)测试
想要实现页面的效果测试可以分为以下步骤:
- 准备好效果图用于比对
- 对待检测的页面进行截图
- 通过像素比对待检测页面截图与效果图得出差异占比
- 自定义通过比例,给出友好提示
前期准备
我们通过 puppeteer 对待检测页面进行截图,通过 blink-diff 来比对两张图的差异,在开始使用前,需要进行如下安装:(当然,这一些都要求你本地已经安装好了 Node.js 环境)
npm install -g puppeteer
npm install -g blink-diff
使用步骤
- 引入已安装的库
const puppeteer = require('puppeteer'),
BlinkDiff = require('blink-diff')
- 通过
puppeteer对待检测页面进行截图
const browser = await puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] });
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 945 });
await page.goto('http://www.baidu.com');
await page.screenshot({ path: imgUrl + 'Screenshots.png', fullPage: true });
//关闭puppeteer
browser.close();
- 通过
blink-diff来比对两张图的差异
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 {
let rel = Math.round((result.differences / result.dimension) * 100)
console.log(diff.hasPassed(result.code) ? '通过' : '失败');
console.log('总像素:' + result.dimension);
console.log('发现:' + result.differences + ' 差异,差异占比' + rel + "%");
if (rel > 20) {
process.exit(1);
}
}
});
比对完成后,会在 images 文件夹中生成对比差异图片,不过,我们暂时并用不到它。我们只需要在 if 中对于比对失败后进行处理即可。
Selenium-webdriver 交互测试
想要实现页面的交互测试可以分为以下步骤:
- 准备待检测的页面
- 规划要检测的交互,比如简单导航条的鼠标悬浮后效果
- 编写检测脚本
- 下载浏览器驱动
- 启动测试与友好提示
前期准备
我们通过 selenium-webdriver 实现页面交互检测(当然,这一些都要求你本地已经安装好了 Node.js 环境)
npm install selenium-webdriver
前期准备第二件非常重要的事情就是下载浏览器驱动,本系列使用 Chrome 浏览器进行测试,下载驱动地址如下
http://chromedriver.storage.googleapis.com/index.html
这里需要特别注意的是:保证下载的 Chrome 浏览器版本与下载的浏览器驱动版本一致。本节中使用的版本为 91.0.4472.19 (这里的版本并不用和我统一,和你本机的 Chrome 版本统一即可)
使用步骤
提前创建一个 index.js 文件,用于写入下列代码。
- 创建
WebDriver实例
无论你是希望测试鼠标单击还是键盘按下,第一件事需要做的都是先创建 WebDriver 实例
var webdriver = require('selenium-webdriver')
var driver = new webdriver.Builder()
.forBrowser('chrome')
.build();
- 打开需要检测的地址
driver.get('http://www.baidu.com');
- 操作页面元素
webdriver.By.id('kw').sendKeys('webdriver');
webdriver.By.id('su').click();
最后,通过 node index.js 就可以执行该文件,可以看到弹出一个 Chrome 浏览器窗口,自动在搜索框中输入 了 webdriver 并单击了搜索按钮。
这一部分我们仅初体验了下 selenium-webdriver 的功能,与了解了它的使用步骤。但仅上面这点功能远远无法实际业务中场景,这就需要了解更多的 API ,滚动鼠标,继续向下看。
常用API
selenium-webdriver 提供了丰富的 API ,想要了解更全面的小伙伴,可以戳这里 。本小节仅对常用的 API 进行介绍。我们可以根据 API 的功能分为以下几类。
- 页面操作
- 定位元素
- 获取元素信息
- 事件操作
接下来,一一认识下
页面操作
| 函数名称 | 描述 | 示例代码 |
|---|---|---|
| get() | 请求指定的 URL | driver.get("www.baidu.com") |
| getTitle() | 检索当前网页标题 | driver.getTitle().then(val => { console.log(val) }); |
| getCurrentUrl() | 检索当前页的URL | driver.getCurrentUrl().then(val => { console.log(val) }); |
| getPageSource() | 检索当前页的源代码 | driver.getPageSource().then(val => { console.log(val) }) |
| quit() | 终止浏览器会话。在调用quit之后,该实例将失效,并且可能不再用于对浏览器发出命令 | driver.quit() |
定位元素
- 通过
By类提供的函数来进行定位元素,By类在 selenium-webdriver 下,常规用法如下:
//第一种
var webdriver = require('selenium-webdriver')
let kw=webdriver.By.id('kw');
//第二种
var webdriver = require('selenium-webdriver'),
By = webdriver.By
let kw=By.id('kw');
By类提供的静态函数:
| 静态函数 | 描述 | 示例代码 |
|---|---|---|
| By.className( name ) | 定位具有特定类名的元素 | By.className('s_ipt') |
| By.css( selector ) | 使用CSS选择器定位元素 | By.css('s_ipt') |
| By.id( id ) | 使用ID选择器定位元素 | By.id('kw') |
| By.linkText( text ) | 定位其可见文本与给定字符串匹配的链接元素 | By.linkText("AboutGoogle") |
| By.name( name ) | 定位name属性具有给定值的元素 | By.name("btnK") |
| By.partialLinkText( text ) | 定位其可见文本包含给定子字符串的链接元素。 | By.partialLinkText("About") |
| By.xpath( xpath ) | 定位与XPath选择器匹配的元素 | By.xpath(“//*[text()=’退出’]) |
- 通过
findElement定位页面上的元素。如果找不到元素,则出错。
var e1 = driver.findElement(By.id('foo'));
var e2 = driver.findElement({id:'foo'});
上面的两句代码作用是等同的,除此之外还可以自定义一个函数来定位元素,比如像下面这样。
var link = driver.findElement(firstVisibleLink);
function firstVisibleLink(driver) {
var links = driver.findElements(By.tagName('a'));
return promise.filter(links, function(link) {
return link.isDisplayed();
});
}
findElement还有一个兄弟就是 findElements 如果你需要搜索页面上的多个元素或需要测试元素是否出现在页面上,可以考虑它。
var e1 = driver.findElements(By.tagName("input")
获取元素信息
定位到元素之后,一个非常高频对操作就是获取元素信息。
| 函数名称 | 描述 | 示例代码 |
|---|---|---|
| getCssValue() | 获取CSS属性值 | element.getCssValue("background-color").then((val) => { console.log(val) } |
| getAttribute() | 获取其他属性 | element.getAttribute("id").then((val) => { console.log(val) } |
事件操作
交互测试最重要的一点就是用户与页面之间的交互,比如鼠标事件、键盘事件。
| 事件 | 描述 | 示例代码 |
|---|---|---|
| click() | 鼠标点击事件 | element.click() |
| doubleClick() | 鼠标双击事件 | element.doubleClick() |
| keyDown() | 键盘按下 | element.keyDown() |
| keyUp() | 键盘抬起 | element.keyUp() |
| sendKeys() | 插入一系列操作以键入所提供的键序列。对于每个键,这将记录一对keyDown和keyUp动作 | element.keyUp.sendKeys('webdriver') |
如果上述的事件无法满足你的需求,比如想要测试鼠标移入与移出事件,应该来做?我将介绍一个万能的函数,绝对可以满足任何功能,也就是 executeScript。
driver.executeScript("document.getElementById('item').onmouseover()")
上述代码会在当前选定的框架或窗口的上下文中执行JavaScript代码片段。脚本片段将作为匿名函数体执行。可以执行代码片段,是不是任何操作都难不住你了。
示例:检测鼠标点击后改变颜色
需求很简单,首先有一个页面,然后上面有一个 div 元素,单击 div 元素后,可以改变 div 的背景颜色,这一系列步骤如何利用自动化检测来完成呢。关键代码如下:
async function checkClick() {
driver.get('http://127.0.0.1:8080');
let item;
await driver.findElements(By.id('item')).then(
function(val) {
//判断元素是否存在
if (val.length == 0) {
console.log("没有找到元素,检测失败");
process.exit(1);
}
item = val[0];
})
item.click();
await item.getCssValue("background-color").then(
function(val) {
if (val != "rgba(255, 239, 161, 1)") {
console.log("检测失败")
process.exit(1);
} else {
console.log("检测成功")
}
})
driver.quit();
}
Jest 单元测试
单元测试是指程序中最小可测试单元,在 JavaScript 中最小可测试单元就是函数了,所以我们重点来介绍如何利用测试框架来测试函数。
前期准备
我们通过 jest 实现函数测试,先需要做的就是安装了
npm install -g jest
使用步骤
- 先来准备一个待测试的 cal.js 文件
function cal(num1, num2, type) {
var result;
switch (parseInt(type)) {
case 0:
result = num1 + num2;
break;
case 1:
result = num1 - num2;
break;
case 2:
result = num1 * num2;
break;
case 3:
result = num1 / num2;
break;
}
return result;
}
module.exports = cal;
文件中包含了一个 cal 函数,实现了一个简易计算器
- 编写测试文件,cal.test.js ,一般命名的时候以在待测试文件的后面跟上 test 结尾
const cal = require('./cal.js');
test('1 + 2 = 3', () => {
expect(cal(1, 2, 0)).toBe(3);
});
test('5 - 2 = 3', () => {
expect(cal(5, 2, 1)).toBe(3);
});
test('1 * 2 = 2', () => {
expect(cal(1, 2, 2)).toBe(2);
});
test('4 / 2 = 2', () => {
expect(cal(4, 2, 3)).toBe(2);
});
- 通过 npm init 一路回车,初始化 package.json 文件,写入如下内容
{
"scripts": {
"test": "jest"
}
}
- 通过
jest cal.test.js启动测试
常用API
在测试过程中,我们使用最多的就是 expect ,它为我们提供了许多匹配的方法,使我们可以轻松实现验证匹配。如果你需要了解更多,可以戳这里。
| 方法 | 描述 | 示例代码 |
|---|---|---|
.toBe(value) | 使用 Object.is 来测试两个值精准相等 | expect(cal(1, 2)).toBe(3) |
.toBeTruthy() | 匹配任何 if 语句为真 | expect(thirstInfo()).toBeTruthy(); |
.toContain(item) | 检查具有特定结构和值的项是否包含在数组中 | expect(list).toContain('obj'); |
最后
到这里,我们就把前端中效果测试、交互测试、单元测试的内容,介绍完了,有需要上述代码,可以戳这里。大家如果还有其他测试场景或者更优+的解决方案,欢迎👏🏻在评论区回复哇~