说明:我们的团队用的umi开发,所以下面会带点umi的代码,因为Umi框架内置了jest库 所以我们无需进行安装
一 jest 常见断言 (测试匹配器) toBe toBe 匹配器有种类似于object.is或者===,精确相等。
1 2 3 4 5 6 7 8 9 10 11 test('测试toBe' , () => { expect(10 ).toBe(10 ); }); test('测试toBe' , () => { const a = {one : 1 } expect(a).toBe( {one : 1 }); });
toEqual 测试对象的内容是否相等,不比较对象的地址,只关心对象的内容是否一致,递归检查对象或数组的每个字段。
1 2 3 4 test('测试toEqual' , () => { const a = {one : 1 } expect(a).toEqual( {one : 1 }); });
toBeNull 测试某个变量是否为null,如果是则Passed,否则failed
1 2 3 4 test('测试toBeNull' , () => { const a = null expect(a).toBeNull(); });
toBeUndefined 和 toBeDefined 测试某个变量是否未定义,如果是则Passed,否则failed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 test('测试toBeUndefined' , () => { const a = undefined ; expect(a).toBeUndefined(); }); test('测试toBeUndefined' , () => { const a = '' ; expect(a).toBeUndefined(); }); test('测试toBeUndefined' , () => { const a = null ; expect(a).toBeUndefined(); }); test('测试toBeDefined' , () => { const a = null ; expect(a).toBeDefined(); }); test('测试toBeDefined' , () => { const a = undefined ; expect(a).toBeDefined(); });
toBeTruthy 测试某个变量是否为真,如果是则Passed,否则failed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 test('测试toBeTruthy' , () => { const a = undefined ; expect(a).toBeTruthy(); }); test('测试toBeTruthy' , () => { const a = null ; expect(a).toBeTruthy(); }); test('测试toBeTruthy' , () => { const a = 0 ; expect(a).toBeTruthy(); }); test('测试toBeTruthy' , () => { const a = 1 ; expect(a).toBeTruthy(); });
toBeFalsy 测试某个变量是否为假,如果是则Passed,否则failed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 test('测试toBeFalsy' , () => { const a = 1 ; expect(a).toBeFalsy(); }); test('测试toBeFalsy' , () => { const a = undefined ; expect(a).toBeFalsy(); }); test('测试toBeFalsy' , () => { const a = null ; expect(a).toBeFalsy(); }); test('测试toBeFalsy' , () => { const a = 0 ; expect(a).toBeFalsy(); }); test('测试toBeFalsy' , () => { const a = 0 ; expect(a).not.toBeFalsy(); });
数字相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 test('测试toBeGreaterThan' , () => { const count = 10 ; expect(count).toBeGreaterThan(9 ); }); test('测试toBeLessThan' , () => { const count = 10 ; expect(count).toBeLessThan(9 ); }); test('测试toBeGreaterThanOrEqual' , () => { const count = 9 ; expect(count).toBeGreaterThanOrEqual(9 ); }); test('测试toBeLessThanOrEqual' , () => { const count = 9 ; expect(count).toBeLessThanOrEqual(9 ); }); test('测试toBeCloseTo' , () => { const firstNumber = 0.1 ; const secondNumber = 0.2 ; expect(firstNumber + secondNumber).toEqual(0.3 ); expect(value).toBe(0.3 ); }); test('测试toBeCloseTo' , () => { const firstNumber = 0.3 ; const secondNumber = 0.4 ; expect(firstNumber + secondNumber).toBeCloseTo(0.7 ); });
字符串相关 1 2 3 4 5 test('测试toMatch' , () => { const str = 'www.baidu.com' ; expect(str).toMatch('baidu' ); expect(str).toMatch(/baidu/ ); });
数组相关 1 2 3 4 5 6 7 8 9 10 test('测试toContain' , () => { const arr = ['dee' , 'lee' ]; expect(arr).toContain('dee' ); }); test('测试toContain' , () => { const arr = ['dee' , 'lee' ]; const data = new Set (arr); expect(data).toContain('dee' ); });
异常情况 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const throwNewErrorFunc = () => { throw new Error ('this is a new error' ); } test('测试toThrow' , () => { expect(throwNewErrorFunc).toThrow(); }); test('测试toThrow' , () => { expect(throwNewErrorFunc).not.toThrow(); }); test('测试toThrow' , () => { expect(throwNewErrorFunc).toThrow('this is a new error' ); expect(throwNewErrorFunc).toThrow(/this is a new error/ ); });
二 测试案例 针对方法的测试:
1.测试方法的两个案例 1.1 src目录下新建一个common/index.js开始添加一个简单的方法 代码如下:
1 2 3 4 5 6 7 8 9 10 function add (a,b ) { return a+b; } function minnus (a,b ) { return a-b; } module .exports={ add, minnus }
在跟目录新建目录 test/common/index.test.js 用于自动测试index.js 中的方法 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import { add , minnus } from '../../src/common/index' describe('测试common/index 文件相关代码' , () => { it('调用 add方法执行 1+1=2' ,()=> { expect(add(1 ,1 )).toBe(2 ) }) it('调用 minnus方法 执行1-1=0' ,()=> { expect(minnus(1 ,1 )).toBe(0 ) }) })
1.2 开发代码
1 2 3 4 5 6 7 8 9 10 11 export default class Counter { constructor ( ) { this .number = 0 ; } addOne ( ) { this .number += 1 ; } minusOne ( ) { this .number -= 1 ; } }
test测试文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import Counter from '../../src/common/index' const counter = new Counter();describe('测试common/index 文件相关代码' , () => { it('测试counter中addOne方法' ,()=> { counter.addOne(); expect(counter.number).toBe(1 ) }) it('测试counter中minusOne方法' ,()=> { counter.minusOne(); expect(counter.number).toBe(0 ) }) })
[图片上传失败…(image-e7c678-1622985585990)]
2.测试异步代码 这是非常常见的通用处理方式,比如你有一个fetchData(callback)的function用来获取数据,并且在获取完成的时候调用callback 函数,你想测试返回的数据是“peanut butter” ,默认情况下当fetchData执行完成的时候Jest的测试就完成了,这并不是你所期望的那样的去运行。
方法一:回调函数 代码示例:fetechData.js文件代码
1 2 3 4 5 6 import request from "umi-request" ;export const fetchData = (fn:any )=> { request.get("http://mock-api.com/RKDx59Ka.mock/test" ).then((response )=> { fn(response.data) }) }
对应的测试用例fetechData.test.js文件代码
1 2 3 4 5 6 7 8 9 10 11 12 import { fetchData} from '../../src/common/fetechData' test('测试 fetchData返回结果为{list: [],state: false}' ,(done )=> { fetchData((data )=> { expect(data).toEqual({ list : [], state : false }) done(); }) })
方法二:使用Promise方法 代码示例:fetechData.js文件代码
1 2 3 4 import request from "umi-request" ;export const fetchData = ()=> { return request.get("http://mock-api.com/RKDx59Ka.mock/test" ) }
对应的测试用例fetechData.test.js文件代码
1 2 3 4 5 6 7 8 9 import { fetchData} from '../../src/common/fetechData' test('测试 fetchData返回结果为{list: [],state: false}' ,()=> { return fetchData().then((response )=> { expect(response.data).toEqual({ list : [],state : false }) }) })
如果要测试404效果可以这样 故意把接口地址改成没用的地址 对应的测试用例fetechData.test.js文件代码
1 2 3 4 5 6 test('测试 fetchData返回结果 404' ,()=> { return fetchData().catch(e => { console .log('e' ,e); expect(e.toString().indexOf('404' )>-1 ).toBe(true ) }) })
方法三:使用 Async/Await 我相信大家对Async/Await 是比较熟悉的,你可以在测试中使用异步和等待。要编写一个async测试,只需在传递到测试的函数前面使用async关键字。例如上面同样的fetchData场景可以使用下面的实现:
对应的测试用例fetechData.test.js文件代码
1 2 3 4 5 6 7 8 9 10 11 import { fetchData} from '../../src/common/fetechData' test('测试 fetchData返回结果为{ code: 0, data: { list: [], state: false } }' ,async ()=>{ let obj; await fetchData().then((res )=> { obj = res; }) const obj2 = { code : 0 , data : { list : [], state : false } } expect(obj).toMatchObject(obj2); })
3.Jest中钩子函数 在jest中,如果测试用例中需要使用到某个对象 或 在执行测试代码的某个时刻需要做一些必要的处理,直接在测试文件中写基础代码是不推荐的,可以使用jest的钩子函数。
钩子函数概念:在代码执行的某个时刻,会自动运行的一个函数。
demo2 上写的 的测试用例可以正常执行,但是addOne()函数调用的次数会影响counter.number的值,就会影响到minusOne方法执行结果。如果你想addOne与minusOne方法调用互不影响时,此时就不得不引入jest的钩子函数。
beforeAll
在所有测试用例执行之前执行一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import Counter from '../../src/common/index' let counter = null ;beforeAll(()=> { console .log('beforeAll' ) counter = new Counter(); }) test('测试counter中addOne方法' ,()=> { counter.addOne(); expect(counter.number).toBe(1 ) }) test('测试counter中minusOne方法' ,()=> { counter.minusOne(); expect(counter.number).toBe(0 ) })
注意:使用beforeAll时,需要把之前const类型修改成let 。此时还是会相互影响,依旧不能达成目标。
beforeEach
在每个测试用例执行之前执行一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import Counter from '../../src/common/index' let counter = null ;beforeAll(()=> { console .log('beforeAll' ) }) beforeEach(()=> { console .log('beforeEach' ) counter = new Counter(); console .log('counter.number' ,counter.number) }) test('测试counter中addOne方法' ,()=> { counter.addOne(); expect(counter.number).toBe(1 ) }) test('测试counter中minusOne方法' ,()=> { counter.minusOne(); expect(counter.number).toBe(0 ) })
此时直接执行npm run test命令时会有一个用例执行失败,执行结果如图所示:
此时返回的结果已经不是0了 ,而是-1!此时addOne 与minusOne两个测试用例已经不会相互影响了。每次执行测试用例前,counter都会初始化!
afterAll
在所有测试用例执行完之后执行一次
afterEach
在每个测试用例执行完成之后执行一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import Counter from '../../src/common/index' let counter = null ;beforeAll(()=> { console .log('beforeAll' ) counter = new Counter(); console .log('counter.number' ,counter.number) }) beforeEach(()=> { console .log('beforeEach' ) counter = new Counter(); }) afterEach(()=> { console .log('afterEach' ) }) afterAll(()=> { console .log('afterAll' ) }) test('测试counter中addOne方法' ,()=> { counter.addOne(); expect(counter.number).toBe(1 ) }) test('测试counter中minusOne方法' ,()=> { counter.minusOne(); expect(counter.number).toBe(-1 ) })
钩子函数执行顺序:
根据上边案例实际打印结果可以看出这四个钩子函数的执行顺序,如下:
(1)beforeAll > (2)beforeEach > (3)afterEach > (4)afterAll
靠前的一次优先执行。
4.snapshot快照测试 1、快照测试 项目中经常有一些配置文件。比如
1 export const generateConfig = ()=> { return { server :'http://localhost' , port :'8080' , domain :'localhost' } }
对应它的测试用例可以这样写 snapshot.test.js
1 2 3 4 5 6 7 8 9 import { generateConfig} from '../../src/common/snapshot' test('测试 generateConfig' , () => { expect(generateConfig()).toEqual({ server : 'http://localhost' , port : '8080' , domain :'localhost' }) })
当配置项不断增加的时候,就需要不断去更改测试用例。
那么我们可以使用快照写测试用例:
1 2 3 4 5 import { generateConfig} from '../../src/common/snapshot' test('测试 generateConfig' , () => { expect(generateConfig()).toMatchSnapshot() })
运行测试用例之后会自动生成快照文件,对应目录如下:
toMatchSnapshot() 会为expect 的结果做一个快照并与前面的快照做匹配。(如果前面没有快照那就保存当前生成的快照即可)
这在配置文件的测试的时候是很有用的,因为配置文件,一般不需要变化。
当然,确实要改配置文件,然后要更新快照,也可。
2、配置文件修改 如果配置文件发生变化,运行时会提示已经发生变化,可以根据提示的命令确定修改文件:‘npm test – -u’
如果添加new Date()这样的参数,配置文件在实时的更新,如何处理呢?对应代码如下: snapshot.js
1 2 3 4 5 6 7 8 export const generateConfig = () => { return { server : 'http://localhost' , port : '8080' , domain :'localhost' , time :new Date () } }
测试用例snapshot.test.js
1 2 3 4 5 6 7 import { generateConfig} from '../../src/common/snapshot' test('测试 generateConfig' , () => { expect(generateConfig()).toMatchSnapshot({ time :expect.any(Date ) }) })
3、行内快照 先需要安装 prettier ,否则行内快照无法执行。
安装命令:npm install prettier
安装完成之后,修改测试用例:
1 2 3 4 5 6 7 import { generateConfig} from '../../src/common/snapshot' test('测试 generateConfig' , () => { expect(generateConfig()).toMatchInlineSnapshot({ time :expect.any(Date ) }) })
运行测试用例之后,会多出一个参数来,表示不匹配 结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { generateConfig } from '../../src/common/snapshot' ;test('测试 generateConfig' , () => { expect(generateConfig()).toMatchInlineSnapshot( { time : expect.any(Date ), }, ` Object { "domain": "localhost", "port": "8080", "server": "http://localhost", "time": Any<Date>, } ` , ); });