说明:我们的团队用的umi开发,所以下面会带点umi的代码,因为Umi框架内置了jest库 所以我们无需进行安装
一 jest 常见断言 (测试匹配器)
toBe
toBe 匹配器有种类似于object.is或者===,精确相等。
test('测试toBe', () => {
// toBe为匹配器 matchers
// toBe相当于js中的 object.is ===
expect(10).toBe(10); // passed
});
test('测试toBe', () => {
const a = {one: 1}
expect(a).toBe( {one: 1}); // failed,因为两个对象的地址是不一样的
});
toEqual
测试对象的内容是否相等,不比较对象的地址,只关心对象的内容是否一致,递归检查对象或数组的每个字段。
test('测试toEqual', () => {
const a = {one: 1}
expect(a).toEqual( {one: 1}); // passed
});
toBeNull
测试某个变量是否为null,如果是则Passed,否则failed
test('测试toBeNull', () => {
const a = null
expect(a).toBeNull(); // passed
});
toBeUndefined 和 toBeDefined
测试某个变量是否未定义,如果是则Passed,否则failed
test('测试toBeUndefined', () => {
const a = undefined;
expect(a).toBeUndefined(); // passed
});
test('测试toBeUndefined', () => {
const a = '';
expect(a).toBeUndefined(); // failed
});
test('测试toBeUndefined', () => {
const a = null;
expect(a).toBeUndefined(); // failed
});
test('测试toBeDefined', () => {
const a = null;
expect(a).toBeDefined(); // passed
});
test('测试toBeDefined', () => {
const a = undefined;
expect(a).toBeDefined(); // failed
});
toBeTruthy
测试某个变量是否为真,如果是则Passed,否则failed
test('测试toBeTruthy', () => {
const a = undefined;
expect(a).toBeTruthy(); // undefined 视为false
});
test('测试toBeTruthy', () => {
const a = null;
expect(a).toBeTruthy(); // null视为false
});
test('测试toBeTruthy', () => {
const a = 0;
expect(a).toBeTruthy(); // 0 视为false
});
test('测试toBeTruthy', () => {
const a = 1;
expect(a).toBeTruthy(); // 1 视为true
});
toBeFalsy
测试某个变量是否为假,如果是则Passed,否则failed
test('测试toBeFalsy', () => {
const a = 1;
expect(a).toBeFalsy(); // failed,因为1 视为true
});
test('测试toBeFalsy', () => {
const a = undefined;
expect(a).toBeFalsy(); // passed,因为undefined 视为false
});
test('测试toBeFalsy', () => {
const a = null;
expect(a).toBeFalsy(); // passed,因为null 视为false
});
test('测试toBeFalsy', () => {
const a = 0;
expect(a).toBeFalsy(); // passed,因为0 视为false
});
test('测试toBeFalsy', () => {
const a = 0;
expect(a).not.toBeFalsy(); // failed,因为0 视为false,但是匹配器要的是真
});
数字相关
test('测试toBeGreaterThan', () => {
const count = 10;
expect(count).toBeGreaterThan(9); // passed,表示希望count这个变量的值比9大
});
test('测试toBeLessThan', () => {
const count = 10;
expect(count).toBeLessThan(9); // failed,表示希望count这个变量的值比9小
});
test('测试toBeGreaterThanOrEqual', () => {
const count = 9;
expect(count).toBeGreaterThanOrEqual(9); // passed,表示希望count这个变量的值大于等于9
});
test('测试toBeLessThanOrEqual', () => {
const count = 9;
expect(count).toBeLessThanOrEqual(9); // passed,表示希望count这个变量的值小于等于9
});
test('测试toBeCloseTo', () => {
const firstNumber = 0.1;
const secondNumber = 0.2;
expect(firstNumber + secondNumber).toEqual(0.3); // 结果是failed,因为js计算浮点数的时
expect(value).toBe(0.3); // 这句会报错,因为浮点数有舍入误差候,有可能会溢出或者说不准确,这种情况下最好用toBeCloseTo
});
test('测试toBeCloseTo', () => {
const firstNumber = 0.3;
const secondNumber = 0.4;
expect(firstNumber + secondNumber).toBeCloseTo(0.7); // passed
});
字符串相关
test('测试toMatch', () => {
const str = 'www.baidu.com';
expect(str).toMatch('baidu'); // passed, 表示str字符串中是否包含baidu这个字符串,是返回passed
expect(str).toMatch(/baidu/); //passed,这里还可以写正则表达式
});
数组相关
test('测试toContain', () => {
const arr = ['dee', 'lee'];
expect(arr).toContain('dee'); // passed, 表示arr数组中是否包含dee这个字符串元素,是返回passed
});
test('测试toContain', () => {
const arr = ['dee', 'lee'];
const data = new Set(arr);
expect(data).toContain('dee'); // passed, 表示arr数组中是否包含dee这个字符串元素,是返回passed
});
异常情况
const throwNewErrorFunc = () => {
throw new Error('this is a new error');
}
test('测试toThrow', () => {
expect(throwNewErrorFunc).toThrow(); // passed, 表示希望throwNewErrorFunc这个方法运行的时候能够抛出一个异常
});
test('测试toThrow', () => {
expect(throwNewErrorFunc).not.toThrow(); // failed, 表示希望throwNewErrorFunc这个方法运行的时候不能够抛出异常
});
test('测试toThrow', () => {
expect(throwNewErrorFunc).toThrow('this is a new error'); // passed, 表示希望throwNewErrorFunc这个方法运行的时候能够抛出一个异常,并且内容是'this is a new error'
expect(throwNewErrorFunc).toThrow(/this is a new error/); // 也可以是正则表达式
});
二 测试案例
针对方法的测试:
1.测试方法的两个案例
1.1
src目录下新建一个common/index.js开始添加一个简单的方法 代码如下:
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 中的方法 代码如下:
import { add , minnus } from '../../src/common/index'
// 尽量每次编写测试用例都用describe包裹进行分块
// 每个测试用例一个it函数代表
// 参数:
// 字符串,代表测试用例名称:常用命名模式“被测对象在什么情况下是什么行为”
// 函数,实际测试用例过程
describe('测试common/index 文件相关代码', () => {
// 测试用例
it('调用 add方法执行 1+1=2',()=>{
// 测试调用后的预期值为2
expect(add(1,1)).toBe(2)
})
it('调用 minnus方法 执行1-1=0',()=>{
// 测试调用后的预期值为0
expect(minnus(1,1)).toBe(0)
})
})
1.2
开发代码
export default class Counter{
constructor(){
this.number = 0 ;
}
addOne(){
this.number += 1;
}
minusOne(){
this.number -= 1;
}
}
test测试文件
import Counter from '../../src/common/index'
const counter = new Counter();
describe('测试common/index 文件相关代码', () => {
it('测试counter中addOne方法',()=>{
counter.addOne(); // 调用addOne方法后 number 执行了 +1 正确结果为1
expect(counter.number).toBe(1) // 表示number值的正确结果为1
})
it('测试counter中minusOne方法',()=>{
counter.minusOne(); // 调用addOne方法后 number 执行了 - 1, 而上面执行了addOne()方法 正确结果为 0
expect(counter.number).toBe(0) // 表示number值的正确结果为 0
})
})
[图片上传失败...(image-e7c678-1622985585990)]
2.测试异步代码
这是非常常见的通用处理方式,比如你有一个fetchData(callback)的function用来获取数据,并且在获取完成的时候调用callback 函数,你想测试返回的数据是“peanut butter” ,默认情况下当fetchData执行完成的时候Jest的测试就完成了,这并不是你所期望的那样的去运行。
方法一:回调函数
代码示例:fetechData.js文件代码
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文件代码
import { fetchData} from '../../src/common/fetechData'
test('测试 fetchData返回结果为{list: [],state: false}',(done)=>{
fetchData((data)=>{
// 对比返回的 data数据是否一致
expect(data).toEqual({
list: [],
state: false
})
done(); // 回调函数,测试用例执行完成执行done
})
})
方法二:使用Promise方法
代码示例:fetechData.js文件代码
import request from "umi-request";
export const fetchData = ()=>{
return request.get("http://mock-api.com/RKDx59Ka.mock/test")
}
对应的测试用例fetechData.test.js文件代码
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文件代码
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文件代码
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 } }
// tomatchobject检查一个JavaScript对象是否匹配一个对象的属性子集
expect(obj).toMatchObject(obj2);
})
3.Jest中钩子函数
在jest中,如果测试用例中需要使用到某个对象 或 在执行测试代码的某个时刻需要做一些必要的处理,直接在测试文件中写基础代码是不推荐的,可以使用jest的钩子函数。
钩子函数概念:在代码执行的某个时刻,会自动运行的一个函数。
demo2 上写的 的测试用例可以正常执行,但是addOne()函数调用的次数会影响counter.number的值,就会影响到minusOne方法执行结果。如果你想addOne与minusOne方法调用互不影响时,此时就不得不引入jest的钩子函数。
beforeAll
在所有测试用例执行之前执行一次
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
在每个测试用例执行之前执行一次
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命令时会有一个用例执行失败,执行结果如图所示:
![](https://img.haomeiwen.com/i11226018/3adb5b5dccbe4b41.jpg)
此时返回的结果已经不是0了 ,而是-1!此时addOne 与minusOne两个测试用例已经不会相互影响了。每次执行测试用例前,counter都会初始化!
afterAll
在所有测试用例执行完之后执行一次
afterEach
在每个测试用例执行完成之后执行一次
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、快照测试
项目中经常有一些配置文件。比如
export const generateConfig = ()=>{ return { server :'http://localhost', port:'8080', domain:'localhost' } }
对应它的测试用例可以这样写 snapshot.test.js
import { generateConfig} from '../../src/common/snapshot'
test('测试 generateConfig', () => {
expect(generateConfig()).toEqual({
server: 'http://localhost',
port: '8080',
domain:'localhost'
})
})
当配置项不断增加的时候,就需要不断去更改测试用例。
那么我们可以使用快照写测试用例:
import { generateConfig} from '../../src/common/snapshot'
test('测试 generateConfig', () => {
expect(generateConfig()).toMatchSnapshot()
})
运行测试用例之后会自动生成快照文件,对应目录如下:
![](https://img.haomeiwen.com/i11226018/ad92435347665760.jpg)
toMatchSnapshot() 会为expect 的结果做一个快照并与前面的快照做匹配。(如果前面没有快照那就保存当前生成的快照即可)
这在配置文件的测试的时候是很有用的,因为配置文件,一般不需要变化。
当然,确实要改配置文件,然后要更新快照,也可。
2、配置文件修改
如果配置文件发生变化,运行时会提示已经发生变化,可以根据提示的命令确定修改文件:‘npm test -- -u’
![](https://img.haomeiwen.com/i11226018/caa73f2c03389d79.jpg)
如果添加new Date()这样的参数,配置文件在实时的更新,如何处理呢?对应代码如下:
snapshot.js
export const generateConfig = () => {
return {
server: 'http://localhost',
port: '8080',
domain:'localhost',
time:new Date()
}
}
测试用例snapshot.test.js
import { generateConfig} from '../../src/common/snapshot'
test('测试 generateConfig', () => {
expect(generateConfig()).toMatchSnapshot({
time :expect.any(Date)// Date String Number等等
})
})
3、行内快照
先需要安装 prettier ,否则行内快照无法执行。
安装命令:npm install prettier
安装完成之后,修改测试用例:
import { generateConfig} from '../../src/common/snapshot'
test('测试 generateConfig', () => {
expect(generateConfig()).toMatchInlineSnapshot({
time :expect.any(Date)// Date String Number等等
})
})
运行测试用例之后,会多出一个参数来,表示不匹配 结果如下:
import { generateConfig } from '../../src/common/snapshot';
test('测试 generateConfig', () => {
expect(generateConfig()).toMatchInlineSnapshot(
{
time: expect.any(Date), // Date String Number等等
},
`
Object {
"domain": "localhost",
"port": "8080",
"server": "http://localhost",
"time": Any<Date>,
}
`,
);
});
网友评论