为什么进行测试
你是否有以下烦恼:
-
当你加班加点完成一个功能后,提交给测试部,立马返回几个bug
-
当你修改完bug后,并检查了好几遍,确保无误后,提交给测试部,有返回几个bug
……
对于以上情境,你是否有过疑问,为什么检查都没问题了还是出现bug?以上这些都是因为没有做好测试。
你可能会问,做了呀,都检查好几遍了。的确,你是测试了,但是你并没有完成测试的闭环。你可能完成测试的一部分,其他的部分并没有完成。既然你说我没完成测试,那何为测试,又怎么进行测试?
什么是测试?
对于前端来说,测试主要是对HTML、CSS、JavaScript进行测试,以确保代码的正常运行。
常见的测试有单元测试、集成测试、端到端(e2e)的测试。
- 单元测试:对程序中最小可测试单元进行测试。我们可以类比对汽车的测试,在汽车组装之前需要对零件进行测试,这种情况下就和单元测试一致。只有零件正常才会进行下一步的组装
- 集成测试:对多个可执行单元组成的整体进行测试。类比于汽车的测试,就相当于测试发动机之前,需要把发动机所需的零件组装在一起对组装后的发动机这个整体进行测试。
- 端到端的测试:从服务端到客户端的测试。这种测试是对服务端和客户端组成的整个系统进行测试,它是能够执行完整流程的测试。
既然知道了有这些测试种类,接下来就说说这些测试应当如何实现。
如何进行测试
测试的方式可以分为人工测试、自动测试。
人工测试:就是让测试部的人员根据业务流程进行操作当某一步或几步出现问题就说明这部分代码有问题。这种测试方式有很明显的不足:
- 它只能测试测试人员看得见的部分,对于测试人员看不见的部分不能测试。比如一些内部的工具函数、逻辑代码等,这些很可能存在问题。
自动测试:利用写好的代码对代码进行测试。这种测试能够弥补人工测试的不足,它的颗粒度是代码级别的,能够准确地识别某个方法的错误
由此,在实际的开发过程中我们可以采用人工测试+自动测试的方式进行测试,力求100%的覆盖测试目标。人工测试暂且不谈,我们先谈谈自动测试的方式实现单元测试、集成测试、e2e测试。本篇博客先讲单元测试。
单元测试
实现单元测试的库和框架有很多,这篇文章以Jest为例进行讲解。
为什么选择jest呢?
优点:
- 比较新
- 基础好
- 速度快:支持单模块测试,减少测试代码
- API简单:容易上手
- 隔离性好:文件之间相互独立
- IDE整合:vscode插件
- 多项目运行:提高效率
- 覆盖率:导出测试覆盖率
使用jest进行单元测试比较简单,因为Jest提供了很多方便的API供开发者使用。
安装
npm install jest -D
生成默认配置
npx jest --init
监听测试文件
执行下面这条命令后,jest会监听测试文件,当文件发生变化时自动执行测试
npx jest --watchAll
jest在匹配测试文件时使用的是glob模式,jest测试文件以 .test.js为后缀。
语法
创建测试分组
describe('description',()=>{});
我们使用describe()方法创建测试分组,在测试分组中可以有多个测试用例。第一个参数是测试分组的描述,第二个参数是回调函数,在回调函数中创建测试用例。
测试用例使用est()方法创建:
test('description',()=>{});
test()方法的第一个参数是测试用例描述,第二个参数是回调函数,在回调函数中进行断言和匹配。
我们可以使用expect()方法创建断言,使用匹配方法来匹配我们期望的值:
expect(true).toBe(true);
匹配方法
在Jest中提供了针对各种数据类型的匹配方法
boolean
toBeFalsy()用来匹配假值
expect(false).toBeFalsy()
toBeTruthy()方法匹配真值
expect(true).toBeTruthy();
object
toEqual()匹配对象,该方法进行的是深度匹配,即匹配对象中的每个键值对
array
toContain()方法匹配数组中的元素
expect(6).toContain([6,7,8]);
string
toMatch()方法匹配字符串中的元素。
expect('hello').toMatch('hello world');
undefined
toBeUndefined()方法匹配undefined的值
let a;
expect(a).toBeUndefined();
与此对应,toBeDefined()用于匹配定义了的值
let a = 1;
expect(a).toBeDefined();
测试异步代码
Jest提供了测试异步代码的方法,用来对异步代码进行测试,测试异步代码的方式有回调函数测试、promise测试、async&await测试
回调函数测试
回调函数测试在test的回调函数中会接收一个done参数,这个参数是一个函数,当异步代码执行完毕后执行done()。
在fetchData中使用fetch发送请求,当请求成功后执行回调函数,在回调函数中测试返回的数据。
test('回调函数',(done)=>{fetchData((responseData)=>{expect(responseData).toEqual({name:'hello'});done();});
})
使用promise测试
使用promise的方式测试代码,需要在test()方法的回调函数中返回promise。
test('异步代码promise方法测试',()=>{return fetchData().then(res => {expect(res).toEqual({name:'hello'}); });
});
async……await测试异步代码
test('async……await测试异步代码',async ()=>{await expect(fetchData()).resolves.toMatchObject({data:{success:true}});
});
Jest的钩子函数
Jest的钩子函数是在测试用例执行的过程中在不同阶段执行的函数,总共有四个分别是beforeAll,beforeEach,afterEach,afterAll。
- beforeAll:在所有的测试用例之前执行
- beforeEach:在每一个测试用例执行之前执行
- afterEach:在每个测试用例执行完成之后执行
- afterAll:在所有的测试用例之后执行
这些钩子函数也有作用域,它们遵守以下规则:
- 钩子函数在父级分组可作用于子集
- 钩子函数同级分组作用域互不干扰
- 先执行外部的钩子函数,再执行内部钩子函数