jest函数单元测试
一、总体概念
jest单元测试的写法为三步,引入测试内容,运行测试内容,最后进行比较,是否达到预期。
Jest中的断言使用expect, 它接受一个参数,就是运行测试内容的结果,返回一个对象,这个对象来调用匹配器(toBe/。。。。) ,
匹配器的参数就是我们的预期结果,这样就可以对结果和预期进行对比了,也就可以判断对不对了
1、两个必会的方法
-
test方法:Jest封装的测试方法,一般填写两个参数,描述和测试方法
-
expect方法 :预期方法,就是你调用了什么方法,传递了什么参数,得到的预期是什么
2、匹配器:
- toBe():绝对相等(===)
- toEqual():简单类型绝对匹配;复杂类型内容结果的匹配
- toBeNull():匹配null
- toBeUndefined():匹配undefined
- toBeDefined():匹配非undefined
- toBeTruthy():匹配转化后为true
- toBeFalsy():匹配转化后为false
- toBeGreaterThan():相当于大于号
- toBeLessThan():相当于小于号
- toBeGreaterThanOrEqual():相当于大于等于号
- toBeLessThanOrEqual():相当于大于等于号
- toBeCloseTo():解决js浮点错误
- toMatch(regExp/string):用正则表达式或者字符串匹配字符串片段
- toContain():匹配数组或者Set中的某一项
- toThrow():匹配异常处理,如果抛出了异常就过测试用例
expect({a:1}).toBe({a:1})//判断两个对象是否相等 expect(1).not.toBe(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'); //匹配数组 function compileAndroidCode() { throw new ConfigError('you are using the wrong JDK'); } test('compiling android goes as expected', () => { expect(compileAndroidCode).toThrow(); expect(compileAndroidCode).toThrow(ConfigError); //判断抛出异常 })
3、describe()测试分组
Jest
为我们提供了一个分组的语法describe()
,创建一个测试集。
这个方法接受两个参数,它的语法和test 的一致,第一个参数也是字符串,对这一组测试进行描述, 第二个参数是一个函数,函数体就是一个个的test 测试。
在jest中,test和it一样,接受两个参数,第一个是字符串,对这个测试进行描述,需要什么条件,达到什么效果。第二个是函数,函数体就是真正的测试代码,jest 要执行的代码
对一个功能进行测试,但它分为多种情况,需要多个test或it, 最好使用descibe() 把多个test 包起来,形成一组测试。只有这一组都测试完成之后,才能说明这个功能是好的。
import {isTrueOrFasle} form './tools' ; describe('true or false', () => { it('should return true when input true', () => { let result = isTrueOrFasle(true); expect(result).toBeTruthy(); // toBeTruthy 匹配器 }) test('should return false when input fasle', () => { let result = isTrueOrFasle(false); expect(result).toBeFalsy(); // toBeFalsy 匹配器 }) })
4、异步代码测试
回调函数/promise/asyn await
参考:https://www.cnblogs.com/SamWeb/p/11454923.html
expect.assertions(1); //断言,表示必须执行一次expect 代码才算执行完
test('test axios async await', async() => { const res = await fetchThreeData(); expect(res.data).toEqual({ success: true }) })
5、Mock函数
有时进行单元测试时,要测试的内容依赖其他内容,比如异步请求,会依赖网络,很可能造成测试达不到效果。 能不能把依赖变成可控的内容?这就用到Mock。Mock就是把依赖替换成我们可控的内容,实现测试的内容和它的依赖项隔离。那怎么才能实现mock呢?使用Mock 函数。在jest中,当我们谈论Mock的时候,其实谈论的就是使用Mock 函数代替依赖。Mock函数就是一个虚拟的或假的函数,所以对它来说,最重要的就是实现依赖的全部功能,从而起到替换的作用。通常,mock函数会提供以下三个功能,来实现替换:函数的调用捕获,设置函数返回值,改变原函数的实现。
在jest 创建一个Mock 函数最简单的方法就是调用jest.fn() 方法。
1.函数的调用捕获。捕获调用指的是这个函数有没有被调用,调用的参数是什么,返回值是什么,通常用于测试回调函数,模拟真实的回调函数。就像下边的forEachFun函数,它接受一个回调函数,每个调用者都会传递不同的回调函数过来,我们事先并不知道回调函数,再者我们测试forEach 的重点是,该函数是不是把数组中的每一项都传递给回调函数了,所以回调函数只要是一个函数就可以了,但该函数必须把调用的信息都保存下来,这就是Mock 函数的调用捕获,为此mock 函数还有一个mock 属性。
测试中就使用jest.fn() 生成的mock 函数来模拟真实的回调函数。
// mock的第一个用处:调用函数的捕获 export const forEachFun = (array: any[], callback: Function) => { array.forEach((i) => callback(i)); }; test('should call callback everyone', () => { const mockFun = jest.fn(); // 模拟函数 const testArr = [1, 2]; forEachFun(testArr, mockFun); console.log(30, mockFun.mock); expect(mockFun.mock.calls.length).toBe(2); });
mock函数mockFun的mock 属性是一个对象,打印结果:
{
calls: [ [ 1 ], [ 2 ] ], instances: [ undefined, undefined ], invocationCallOrder: [ 1, 2 ], results:[
{ type: 'return', value: undefined }, { type: 'return', value: undefined }
]
}
- calls 保存的就是调用状态。calls 是一个数组,每一次的调用都组成数组的一个元素,在这里调用了两次,就有两个元素。每一个元素又是一个数组,它则表示的是函数调用时的参数,因为每次的调用都传递了一个参数给函数,所以数组只有一项。如果有多个参数,数组就有多项,按照函数中的参数列表依次排列。这时候,就可以做断言,函数调用了几次,就判断calls.length. expect(mockFun.mock.calls.length).toBe(2) 就是断言函数是不是调用了两次。expcet(mockFun.mock.calls[0][0]) .toBe(1)就是断言第一次调用的时候传递的参数是不是1. 可能觉得麻烦了, 的确有点麻烦了,幸好,jest 对函数的mock参数进行了简单的封装,提供了简单的匹配器:
toHaveBeenCalled()/toBeCalled():用来判断mock函数是否被掉用过;
toHaveBeenCalledTimes(number)/toBeCalledTimes(number):用来判断mock函数调用过几次;
toHaveBeenCalledWith(arg1,arg2,...)/toBeCalledWith(arg1,arg2,...):用来判断是否使用了特定参数调mock函数
..... 参考官网
test('should call callback everyone', () => { const mockFun = jest.fn(); const testArr = [1, 2]; forEachFun(testArr, mockFun); expect(mockFun).toHaveBeenCalled(); });
- results保存的就是返回值。
2.设置函数返回值。有的时候,不想调用函数,直接获取到函数的返回值就可以了,比如异步函数, 以fetchData 为例,它直接返回一个promise 就好了,根本没有必要请求服务器。
mock函数有mockReturnValue(), 它的参数就是返回值。不过它不能返回promise.。
可以使用mockResolvedValue直接返回promise的值. 对fetchData 进行mock, 然后设置它的mockResolvedValue()
test('should return data when fetchData request success', () => { const fetchData = jest.fn(); fetchData.mockResolvedValue({name: 'sam'}) return fetchData().then(res => { expect(res).toEqual({name: 'sam'}) }) })
3.改变函数实现。 有时不想使用默认的mock函数jest.fn(),尤其是测试回调函数的时候,你想提供回调函数实现,比如上面的forEach, 确实写一个真实的回调函数进行测试,心里更有底一点。mock 函数实现也有两种方法,jest.fn() 可以接受一个参数,这个参数就可以是一个函数实现。forEach 中的mock 函数就可以成mock 函数提供了一个方法mockImplementation(), 它的参数也是一个函数实现,使用mockImplementation() 来mock fetchData,让它返回{name: 'sam'}
注:VSCode的终端窗口中输入yarn test
就可以进行测试了
- 配置jest :npx jest --init
- 生成代码覆盖率:npx jest --coverage
- Jest识别三种测试文件:
- 测试文件后缀为js,jsx,ts,tsx
- 测试文件需要放在tests/unit/目录下或者是/__tests__/目录下
- 以xx.test.js/...结尾的文件,以xx.spec.js/...结尾的文件,
只要满足这三个要求的测试文件,使用运行jest时就会自动执行