引子
在之前分享的编辑器相关文章里,讲到了语雀是全栈js开发的,并且号称团队没有测试工程师,参考文章里面有放关于全栈 JavaScript 测试的相关总结分享,今天我们就聊一聊前端的测试。
单元测试
- 定义:
在计算机编程中,单元测试(Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。
程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。
- 意义
- 提升能力
- 提升效率:及早发现问题
- 追求卓越:有助于开发人员去思考代码结构的设计,让代码更加有利于测试
- 覆盖全面:能够覆盖到QA测试覆盖不到的情况
- 大型项目,协同开发,公共抽离组件会出现一些问题,有了单测后,更强壮,更易读,升级时,回归测试任务少,提升公共组件的质量。
- 测试方法论 TDD&BDD
- TDD:Test-driven development 测试驱动开发
TDD 从测试的角度来检验整个项目。大概的流程是先针对每个功能点抽象出接口代码,然后编写单元测试代码,接下来实现接口,运行单元测试代码,循环此过程,直到整个单元测试都通过。 - BDD:Behavior Driven Development 行为驱动开发
是通过编写行为和规范来驱动软件开发。更注重功能本身。
- 原则:FIRST
- F fast 快速 过程快
- I Isolate 隔离 测试用例应当独立执行,避免一个测试结果依赖于另外一个。需要把测试分解成尽可能小的单元,这将帮我们确定在错误发生时的确切代码位置。
- R Repeatable 可重复:多次运行测试应该产生相同的结果,如果不确定,就不知道哪些结果是有效的,哪些又是无效的。另外,反复性可以确保我们的测试不依赖于外部因素(诸如网络或 CPU 负载)。
- S Self-validating 自我验证无需人工
- T Timely 及时,上线前
测什么
结果正确
边界条件检查
反向关联(加密解密 读写操作)
强制错误条件测试的核心
断言(assert)即表达程序设计人员对于系统应达到状态的一种预期。
前端测试框架:
帮助我们把测试代码抽离出来,作为一个整体结构化地去设计测试用例,放置到专门测试文件,然后也可以实现自动运行以及显示测试结果。
介绍:
Mocha,Jest,Jasmine 可以说都是测试框架,可以达到之前说的,抽离,结构化,自动运行等等,其中 Jest 是一个相当全面的框架,且零配置,同时需要注意的是 Jest 也是基于 Jasmine 的,所以 Jest 的一些语法和 Jasmine 一模一样,连报错有时都会相同。
Chai,Should 都属于断言库,它们的 API 相对测试框架自带的断言 API 更加语义化,更好用,可以和上面的测试框架结合着用。
Karma 是一个 Test-Runner,可以说是凌驾测试框架之上,可以让给你的浏览器自动跑所有的测试用例。
Enzyme是由airbnb开发的React单元测试工具。它扩展了React的TestUtils并通过支持类似jQuery的find语法可以很方便的对render出来的结果做各种断言。
Jest
https://github.com/facebook/jest star 36.2k
Jest是Facebook开发的一个测试框架,它集成了测试执行器、断言库、spy、mock、snapshot和测试覆盖率报告等功能。React项目本身也是使用Jest进行单测的,因此契合度相当高。
- 自带断言函数
exspect(运行结果).toBe(期望的结果);
//常见断言方法
expect({a:1}).toBe({a:1})//判断两个对象是否相等
expect(1).not.toBe(2)//判断不等
expect({ a: 1, foo: { b: 2 } }).toEqual({ a: 1, foo: { b: 2 } })//判断相等
expect(n).toBeNull(); //判断是否为null
expect(n).toBeUndefined(); //判断是否为undefined
expect(n).toBeDefined(); //判断结果与toBeUndefined相反
expect(n).toBeTruthy(); //判断结果为true
expect(n).toBeFalsy(); //判断结果为false
expect(value).toBeGreaterThan(3); //大于3
expect(value).toBeGreaterThanOrEqual(3.5); //大于等于3.5
expect(value).toBeLessThan(5); //小于5
expect(value).toBeLessThanOrEqual(4.5); //小于等于4.5
expect(value).toBeCloseTo(0.3); // 浮点数判断相等
expect('Christoph').toMatch(/stop/); //正则表达式判断
expect(['one','two']).toContain('one'); //包含
- 支持异步测试
- 支持Dom测试
- Mock Functions
一个简单的示例:
- 安装
npm install --save-dev jest
- 新建测试文件
unit.test.js
import {dealData} from "./src/util/Util";
describe("dealData测试",()=>{
test('dealData 1 GB to equal 1 GB', () => {
expect(dealData(1, 'GB')).toEqual({
value:1,
unit:'GB'
});
});
//输入数据大于1024GB则转换为TB
test('dealData 2048 GB to equal 2 TB', () => {
expect(dealData(2048, 'GB')).toEqual({
value:2,
unit:'TB'
});
});
//输入数据大于1024*1024*10GB则转换为PB
test('dealData 20971520 GB to equal 20 PB', () => {
expect(dealData(20971520, 'GB')).toEqual({
value:20,
unit:'PB'
});
});
//PB为最大单位,不进行进一步处理
test('dealData 419430400 GB to equal 20 PB', () => {
expect(dealData(41943040000, 'GB')).toEqual({
value:40000,
unit:'PB'
});
});
//其他单位不进行转换
test('dealData 1 KB to equal 1 KB', () => {
expect(dealData(1, 'KB')).toEqual({
value:1,
unit:'KB'
});
});
//接口异常时数据不进行转换
test('dealData -- to equal --', () => {
expect(dealData('--', '')).toEqual({
value:'--',
unit:''
});
});
})
注:dealData 是一个抽离出来的工具函数,帮助动态处理业务数据单位
- 在
package.json
中增加代码
{
"scripts": {
"test": "jest"
}
}
- 运行
npm run test
image.png
Enzyme
https://github.com/enzymejs/enzyme star 19.7k
React测试必须使用官方的测试工具库,但是它用起来不够方便,所以有人做了封装,推出了一些第三方库,其中Airbnb公司的Enzyme最容易上手。
项目实战时,结合jest和enzyme。
Enzyme为我们提供来三种渲染组件的方法shallow、render、mount。
shallow 方法就是官方的shallow rendering的封装。
render 方法将React组件渲染成静态的HTML字符串,然后分析这段HTML代码的结构,返回一个对象。它跟shallow方法非常像,主要的不同是采用了第三方HTML解析库Cheerio,它返回的是一个Cheerio实例对象。
mount方法用于将React组件加载为真实DOM节点。
测试登录交互例子:
import Login from 'pages/Login';
import React from 'react';
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import { mount } from 'enzyme';
configure({ adapter: new Adapter() });
const wrapper = mount();
describe('', () => {
it('标题显示', () => {
const title = wrapper.find('.title').text();
expect(title).toBe('登录');
})
const accountInput = wrapper.find('.account').find('input');
const accountTitle = wrapper.find('.account .name').find('span');
it('输入不合法账号', () => {
const event = {
target: {
value: 'abc123'
}
}
accountInput.simulate('change', event);
expect(accountTitle.text()).toBe('账户输入错误,请重新输入');
})
})
uirecorder
https://github.com/alibaba/uirecorder star 1.8k
UI Recorder 是一款面向多端的 UI 自动化录制工具,类似于Selenium IDE 但比Selenium IDE 更加强大!
UI Recorder 非常简单易用,零成本解决测试回归问题。
功能:
- 支持所有用户行为: 键盘事件, 鼠标事件, alert, 文件上传, 拖放, svg, shadow dom
- 全平台支持,移动端 Android, iOS 录制, 基于 Macaca 实现
- 无干扰录制: 和正常测试无任何区别,无需任何交互
- 录制用例存储在本地
- 支持丰富的断言类型: val,text,displayed,enabled,selected,attr,css,url,title,cookie,localStorage,sessionStorage
- 支持图片对比
- 支持强大的变量字符串
- 支持公共测试用例: 允许用例中动态调用另外一个
- 支持并发测试
- 支持多国语言: 英文, 简体中文, 繁体中文
- 支持单步截图
- 支持HTML报告和JUnit报告
- 全系统支持: Windows, Mac, Linux
- 基于Nodejs的测试用例: jWebDriver
官方教程:https://www.yuque.com/artist/uirecorder/yd7ztf
- 初始化工程
- 开始录制,进行界面操作,可以添加断言,悬停等等
- 结束录制,自动生成脚本文件
- 利用已有脚本文件进行回归测试