Jest 快速入门

什么是 Jest

Jest 是一个前端单元测试框架, 由 Facebook 退出, 是目前前端最火热的框架. 它具有以下特点:

  1. Fast 天下武功, 唯快不破
  2. Opinionated 开箱即用
  3. Watch Mode 守护模式
  4. Shapshot Testing 快照测试

基础使用

这里首先创建一个前端项目, 这里直接 yarn init 一个出来.

随后, 在项目中安装 jest:

1
yarn add jest -D

这里从一个最简单的函数开始, 直接创建一个 math.js 工具文件出来, 并且写入以下代码:

1
2
3
const mySum = (a, b) => a + b

export {mySum}

随后就可以创建测试文件了. 测试文件需要使用 .test.js 结尾. 一般来说, 我们创建 文件名.test.js.

1
2
3
4
5
6
7
const mySum = require('./math')

// 直接使用 test函数, 全局可用的API
test("这里写描述信息, 用来测试1 + 2 = 3", () => {
// 回调函数里面写测试代码
expect(mySum(1, 2)).toBe(3)
})

随后, 我们可以添加一个命令, 来运行 jest:

1
2
3
4
5
6
7
8
9
10
11
12
{
"name": "jest-study",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"test": "jest"
},
"devDependencies": {
"jest": "^30.0.5"
}
}

随后, 直接运行 yarn test 就可以看到效果了:

1
2
3
4
5
6
7
8
9
10
$ jest
PASS ./math.test.js
√ 这里写描述信息, 用来测试1 + 2 = 3 (3 ms)

Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.396 s
Ran all test suites.
Done in 1.47s.

这里会显示绿色的 passed, 就保证了 sum 函数符合预期. 假如后面函数改错了, 改没了, 那么就会出现失败的情况, 可以看到没有通过的报错信息是什么, 方便我们定位问题.

判断方法

jest 最核心的就是后面的 toBe 之类的判断方法, 不过不止这些, 还有更多的内容.

toBe

这个是严格相等, 对于普通的字符串, 数字, 布尔值可以这么判断, 但是如果是数组, 则不能这么写. 例如下面写一个返回值为函数的代码.

1
2
3
4
5
function addToArr(arr, ...args) {
arr.push(...args)
}

module.exports = addToArr

对应的测试文件如下:

1
2
3
4
5
6
7
const addToArr = require('./addToArr')

test("添加4, 5, 6到数组 [1, 2, 3]中", () => {
const arr = [1, 2, 3]
addToArr(arr, 4, 5, 6)
expect(arr).toBe([1, 2, 3, 4, 5, 6])
})

毫无疑问的 failed 了:

这里的解释其实也写了, 应该深层次的比较. 所以我们这里要用别的.

toEqual

为了解决上面数组无法比较的问题, 我们需要引入 toEqual 进行深层次的拷贝. 上面的代码改成下面这样, 即可通过测试.

1
2
3
4
5
6
7
const addToArr = require('./addToArr')

test("添加4, 5, 6到数组 [1, 2, 3]中", () => {
const arr = [1, 2, 3]
addToArr(arr, 4, 5, 6)
expect(arr).toEqual([1, 2, 3, 4, 5, 6])
})

这就是 toEqual 的用法.

not

其实就是顾名思义, 不等于. 还是上面的代码, 通过 .not.toBe, 就可以通过测试了.

1
2
3
4
5
6
7
const addToArr = require('./addToArr')

test("添加4, 5, 6到数组 [1, 2, 3]中", () => {
const arr = [1, 2, 3]
addToArr(arr, 4, 5, 6)
expect(arr).not.toBe([1, 2, 3, 4, 5, 6])
})

这里仅仅作为演示, 我们不推荐这样进行判断.

更多方法

mockFn

无论如何, 我们的函数不一定会返回, 我们可能只希望看看一个函数运行是否正常, 运行次数是否为期待的期数.

例如下面这个, 对 map 进行一个包装:

1
2
3
4
5
function map(arr, cb) {
return arr.map(cb)
}

module.exports = map

这里的测试就需要用到 mock 了.

1
2
3
4
5
6
7
8
const map = require('./map')

test("map [1, 2, 3], 回调函数执行三次", () => {
const mockFn = jest.fn(x => x * 2)
map([1, 2, 3], mockFn)
// 这里可以使用length判断运行次数
expect(mockFn.mock.calls.length).toBe(3)
})

另外, 如果需要测试别的, 也可以继续往后写:

1
2
3
4
5
6
7
8
9
10
11
12
const map = require('./map')

test("map [1, 2, 3], 回调函数执行三次", () => {
const mockFn = jest.fn(x => x * 2)
map([1, 2, 3], mockFn)
// 这里可以使用length判断运行次数
expect(mockFn.mock.calls.length).toBe(3)
// 可以多次测试的, 比如测试每次函数的调用结果是否正确
expect(mockFn.mock.results[0].value).toBe(2)
expect(mockFn.mock.results[1].value).toBe(4)
expect(mockFn.mock.results[2].value).toBe(6)
})

分组

一个文件有很多测试的话, 我们可以使用 describe 进行一个分组.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const map = require('./map')

describe('测试map函数是否运行正常', () => {
test("map [1, 2, 3], 回调函数执行三次", () => {
const mockFn = jest.fn(x => x * 2)
map([1, 2, 3], mockFn)
// 这里可以使用length判断运行次数
expect(mockFn.mock.calls.length).toBe(3)
})

test("map [1, 2, 3], 回调结果正常", () => {
const mockFn = jest.fn(x => x * 2)
map([1, 2, 3], mockFn)
expect(mockFn.mock.results[0].value).toBe(2)
expect(mockFn.mock.results[1].value).toBe(4)
expect(mockFn.mock.results[2].value).toBe(6)
})
});

再次运行, 就可以看到已经将测试进行分组了.

测试覆盖率

可以在执行 jest 命令的时候, 后面加上一个参数, 用来生成测试覆盖率的情况.

1
2
3
4
5
{
"scripts": {
"test": "jest --coverage"
},
}

再次运行 test, 就可以看到生成的表格了.

另外, 也会生成一个 coverage 目录, 这个目录存放的就是网页版的覆盖率图了, 可以直接打开看看:

还是十分专业的.