入门前端自动化测试-jest-基础
Posted lin-fighting
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了入门前端自动化测试-jest-基础相关的知识,希望对你有一定的参考价值。
jest基础
匹配器
jest默认环境是node
如果想在jest环境使用esmodule,需要借助@babel/core转化工具, @babel/preset-env指明如何转化
"presets": [
[
"@babel/preset-env",
"targets":
"node": "current"
]
]
只需要一个项目中安装jest,然后执行 npx jest --watch,jest就会自动将所有的.test.js结尾的文件中的测试用例执行。
jest提供了很多匹配器,最基本的使用
test('测试加法', ()=>
// toBe 类似 Object.is(a,b),适用于普通值
expect(10).toBe(10)
)
test('测试内容', ()=>
// 匹配对象使用toEqual,递归遍历使用toBe, 加上.not就是取反
expect(a: 1).toEqual(a: 1)
)
如toBe, toEqual,等等还有很多。当执行npx jest --watch的时候,jest就会执行这些测试用例并返回结果。
PASS ./9.test.js
√ 测试加法 (2ms)
√ 测试内容 (1ms)
Snapshot Summary
› 1 snapshot file obsolete from 1 test suite. To remove it, press `u`.
↳ • __snapshots__学习\\demo.test.js.snap
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 1 file obsolete, 0 total
Time: 0.987s
Ran all test suites related to changed files.
测试通过。
还有更多的适配器如:
expect(10).toBeGreaterThan(9) //大于9
expect( 0.1 + 0.2).toBeCloseTo(0.3) //解决精度问题
expect('abcd').toMatch(/ab/) //匹配字符串
expect([1,2]).toContaine(1) //数组或者集合是否包含某个数
expect(()=>throw new Error('123')).toThrow() //抛出异常, 没有toThrow不会抛出异常
expect(()=>throw new Error('123')).toThrow('123') //是否抛出123异常,是的话测试通过
更多适配器可以查看官网。
命令行工具
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 file obsolete, 0 total
Time: 2.302s, estimated 3s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
执行完后,下面会提示输入W显示更多指令
Watch Usage
› Press a to run all tests.
› Press f to run only failed tests.
› Press p to filter by a filename regex pattern.
› Press t to filter by a test name regex pattern.
› Press u to update failing snapshots.
› Press q to quit watch mode.
› Press Enter to trigger a test run.
-
a执行所有测试 默认 jest --watchAll 就是打开了a模式
-
f只测试失败的测试
-
p 根据文件名字正则表达式,匹配更改的test
-
t 根据文件名字,过滤某些test
-
u 更改失败的快照(后面会讲)
-
o 配合git,只执行有修改的文件 默认–watch就是打开了。
测试异步
// 默契请求的方法
const fetchData = () =>
new Promise((resolve, reject) =>
setTimeout(() =>
resolve( success: true );
, 2000);
);
export fetchData ;
因为默认情况下,Jest 测试在执行结束后完成。
test('the data is peanut butter', () =>
fetchData().then(res=>
expect(res).toEqual(success: true)
)
);
那么其实不管输入什么,该test就会执行正确。
正确测试异步方法:
1 done
test("fetchData 返回结果为success: true", (done) =>
fetchData()
.then((res) =>
expect(res).toEqual( success: true );
done();
)
.catch((err) =>
done(err);
);
);
只有调用done,才会正确测试该test。
2 promise
正常使用promise.then,但是一定要return。而且可以使用expect.assertions(1)表示至少执行一次expect。
因为有些场景,我们只俘获了catch,并无俘获then,那么Promsie成功的时候,这个测试将会没有执行。所以需要这个来确定至少执行一次。否则失败。
test("fetchData 返回结果为success: true", () =>
expect.assertions(1) //至少执行一次expect,如果没跑也会报错
return fetchData()
.then((res) =>
expect(res).toEqual( success: true );
)
.catch((err) =>
expect(err).toMatch("error");
);
);
3 resolves/reject
// 可以.resolves在您的期望语句中使用匹配器,Jest 将等待该承诺解决。如果 promise 被拒绝,测试将自动失败。
test("fetchData 返回结果为success: true", () =>
return expect(fetchData()).resolves.toMatchObject(
success: true,
);
// 期望一个 Promise 被拒绝,使用.rejects匹配器。如果promise被拒绝,就执行,如果promise成功执行,则该jest默认失败
return expect(fetchData()).rejects.toThrow()
);
4 asycn/await
记得错误处理
test("fetchData 返回结果为success: true", async () =>
try
const data = await fetchData();
expect(data).toEqual(
success: true,
);
catch (err)
// 抛出异常
expect(err).toThrow()
);
钩子函数
jest在执行test用例的时候,提供了很多钩子,用来初始化一些东西或者做一些销毁工作,如执行前调用,执行后调用等等。如
beforeAll(()=>
console.log('beforeAll');
)
afterAll(()=>
console.log('afterAll');
)
他们分别会在所有的钩子执行前执行和执行后执行。
beforeEach(()=>
console.log('beforeEach');
)
afterEach(()=>
console.log('afterEach');
)
beforeEach和afterEach分别会在每个test执行之前和之后运行。
分组
相同的意义的测试用例可以写在同一个分组中,相当于ts的namespace,jest提供了describe
describe("测试加法", () =>
beforeAll(() =>
console.log("内部 eforeAll");
);
afterAll(() =>
console.log("内部 afterAll");
);
afterEach(() =>
console.log("内部 afterEach");
);
beforeEach(() =>
console.log("测试加法 beforeEach");
counter = new Counter();
);
test("test Counter addOne", () =>
counter.addOne();
expect(counter.number).toBe(1);
console.log("addOne");
);
);
如,describe内部的钩子,只会在内部执行。而整个test文件,可以看作是外层包装了一个descrbie(‘xx’,()=>// test文件的内容)
那么desceibe内部的钩子和外部的钩子,执行顺序是怎样呢?
以下面的为例子
beforeAll(() =>
console.log("外部 beforeAll");
);
afterAll(() =>
console.log("afterAll");
);
beforeEach(() =>
console.log("外部 beforeEach");
);
afterEach(() =>
console.log("外部 afterEach");
);
describe("测试加法", () =>
beforeAll(() =>
console.log("内部 eforeAll");
);
afterAll(() =>
console.log("内部 afterAll");
);
afterEach(() =>
console.log("内部 afterEach");
);
beforeEach(() =>
console.log("测试加法 beforeEach");
);
test("test Counter addOne", () =>
console.log("descrbie内部的测试 addOne");
);
);
执行顺序就是
外部 beforeAll
内部 beforeAll
外部 beforeEach
测试加法 beforeEach
descrbie内部的测试 addOne
内部 afterEach
外部 afterEach
内部 afterAll
外部 afterAll
技巧就是:外部beforeAl > 内部beforeAll > 外部beforeEach > 内部beforeEach > test执行 > 内部afterEach > 外部 afterEach > 内部 afterAll > 外部 afterAll
mock
jest提供了mock的功能,可以模拟请求数据的函数,模拟返回的数据。
其中
jest.fn 返回一个mock函数,1 捕获函数的调用和返回结果, 2 可以自由地设置返回结果 3模拟axios请求返回特定数据
test("测试", () =>
const func = jest.fn((a) =>
return a + 4;
);
// func.xxx有很多语法。
// func.mockImplementtation((a)=>return a+4)跟jest.fn(fn)的fn一样,函数执行的时候的逻辑
// func.mockImplementationOnce(()=>) 只执行一次。
// mockReturnValue('a') 每次调用都返回a
func.mockReturnValueOnce("11111").mockReturnValueOnce("2222"); //第1,2次执行func的时候,返回了1111, 2222
func(1);
func(2);
func(3);
func(4);
// func是否被执行过
//expect(func).toBeCalled()
//expect(func.mock.calls[0]).toEqual([1])
expect(func.mock.results);
console.log(func.mock);
/**
console.log(func.mock);
可以通过func.mock.calls.length判断调用多少次,以及传参, fn.results判断返回值
calls: [ [ 1 ], [ 2 ], [ 3 ], [ 4 ] ],
instances: [ undefined, undefined, undefined, undefined ], func运行的时候,this指向
invocationCallOrder: [ 1, 2, 3, 4 ],
results: [
type: 'return', value: '11111' ,
type: 'return', value: '2222' ,
type: 'return', value: 7 ,
type: 'return', value: 8
]
);
func执行的时候,实际上是jest.fn()在执行,func.mock提供了很多内部,包括调用多少次,每次的入参,每次的返回等等。并且可以通过func.mockReturnValueOnce(‘xx’)默契一次func调用的时候的返回。
jest.mock可以改变函数的实现
// 3 改变函数的内部实现
import axios from 'axios'
jest.mock('axios')
test.only('测试 axios',async()=>
axios.get.mockResolvedValue(data: '123') //模式请求返回数据
console.log('axios', axios);
await axios.get('xxx').then(data=>
console.log(data);
expect(data).toEqual(data: '123') //断言
)
)
jest.mock(‘axios’)可以将axios上的所有属性变成jest.fn。打印出来的axios是
console.log 9.test.js:5
axios <ref *1> [Function: wrap]
_isMockFunction: true,
getMockImplementation: [Function (anonymous)],
mock: [Getter/Setter],
mockClear: [Function (anonymous)],
mockReset: [Function (anonymous)],
mockRestore: [Function (anonymous)],
mockReturnValueOnce: [Function (anonymous)],
mockResolvedValueOnce: [Function (anonymous)],
mockRejectedValueOnce: [Function (anonymous)],
....
axios已经被改造了。可以通过axios.get.mockResolvedValue(data: ‘123’)模式get请求返回的数据,测试如下:
PASS ./9.test.js
√ 测试 axios (10ms)
console.log 9.test.js:6
data: '123'
Snapshot Summary
› 1 snapshot file obsolete from 1 test suite. To remove it, press `u`.
↳ • __snapshots__学习\\demo.test.js.snap
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 file obsolete, 0 total
Time: 2.568s, estimated 3s
Ran all test suites related to changed files.
测试通过,get请求返回的data就是data: ‘123’
自己改造函数
jest执行测试用例的时候,并不希望真正的发送请求。所以可以通过jest.mock模拟。
假设fetchData是代码中真正发送请求的函数,我们想验证这个函数的正确性,又不想发送请求。可以在根目录下创建__mock__
文件夹,命名相同的文件名字,如demo.js然后实现
// __mock__/demo.js
const fetchData = () =>
new Promise((resolve, reject) =>
setTimeout(() =>
resolve( success: true );
, 2000);
);
export fetchData
接着引入fetchData的时候
import fetchData from './demo'
jest.mock('./demo') //该语句的用途是去__mock__下面找demo文件,然后看有没有导出的方法,这样上一条语句导入的fetchData就变成了从__mock__/demo.js中导入的我们自己模拟的fetchData,然后去执行。
// 有多个引入的时候,可以通过const aa = jest.requireActual('./demo'),这样,aa就回去真正的demo文件下引入了。
test('xxx',()=>
return fetchData().then(res=>.....)
)
其次,如果jest.mock(’./demo’)找不到__mock__
下的文件的时候,jest内部就会自动模拟,如上面的axios。
mock模拟类
// util.js
class Util
a()
setTimeout(()=>
console.log(123);
, 3000)
b()
setTimeout(()=>
console.log(123);
, 5000)
export default Util
// test.js
import Util from './util'
jest.mock('./util') //因为我们没有在__mock__下定义该文件, jest.mock发现util是一个类,就自动把类的构造函数和方法变成Jest.fn()
// const Util = jest.fn()
// Util.prototype.a = jest.fn()
// Util.prototype.b = jest.fn()
我们也可以自己定义
// __mock__/util.js
const Util = jest.fn()
Util.prototype.a = jest.fn()
Util.prototype.b = jest.fn()
这样jest.mock(’./util’)就会来这里找。
甚至可以
// test.js
jest.mock('./util', ()=>
const<以上是关于入门前端自动化测试-jest-基础的主要内容,如果未能解决你的问题,请参考以下文章