2022-09-27 23:09:51 +08:00
|
|
|
|
Jest 备忘清单
|
|
|
|
|
===
|
|
|
|
|
|
2022-10-14 18:02:55 +08:00
|
|
|
|
Jest 是一款优雅、简洁的 JavaScript 测试框架,这里介绍了它的入门和 一些 API 的索引。
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
入门
|
|
|
|
|
----
|
2022-10-08 03:44:42 +08:00
|
|
|
|
<!--rehype:body-class=cols-6-->
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
### 介绍
|
2022-10-08 03:44:42 +08:00
|
|
|
|
<!--rehype:wrap-class=col-span-2 row-span-2-->
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
2022-10-15 13:32:34 +08:00
|
|
|
|
[Jest v29](https://jestjs.io/) 是一款优雅、简洁的 JavaScript 测试框架。
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
2022-10-02 03:23:32 +08:00
|
|
|
|
- `无需配置` 大多数 JS 项目中即装即用,无需配置
|
|
|
|
|
- `优秀接口` 从 `it` 到 `expect` - Jest 将工具包整合在一处。文档齐全、不断维护,非常不错。
|
|
|
|
|
- `隔离的` 并行进行测试,发挥每一丝算力。
|
|
|
|
|
- `快照` 轻松编写持续追踪大型对象的测试,并在测试旁或代码内显示实时快照。
|
|
|
|
|
- `代码覆盖` 无需其他操作,您仅需添加 `--coverage` 参数来生成代码覆盖率报告。
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
2022-10-08 03:44:42 +08:00
|
|
|
|
### 快速开始
|
|
|
|
|
<!--rehype:wrap-class=col-span-2 row-span-2-->
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm install --save-dev jest
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Add to `package.json`
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
"scripts": {
|
|
|
|
|
"test": "jest"
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
运行你的测试
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm test -- --watch
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
查看: [Getting started](https://jestjs.io/docs/getting-started)
|
|
|
|
|
|
|
|
|
|
### 编写测试
|
2022-09-28 13:35:52 +08:00
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
2022-10-08 03:44:42 +08:00
|
|
|
|
```js
|
|
|
|
|
describe('My work', () => {
|
|
|
|
|
test('works', () => {
|
|
|
|
|
expect(2).toEqual(2)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### BDD 语法
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
```js {2}
|
|
|
|
|
describe('My work', () => {
|
|
|
|
|
it('works', () => { // `it`是`test`的别名
|
|
|
|
|
···
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 测试结构
|
|
|
|
|
<!--rehype:wrap-class=col-span-3 row-span-2-->
|
|
|
|
|
|
2022-09-27 23:09:51 +08:00
|
|
|
|
```js
|
|
|
|
|
describe('makePoniesPink', () => {
|
|
|
|
|
beforeAll(() => {
|
|
|
|
|
/* 在所有测试之前运行 */
|
|
|
|
|
})
|
|
|
|
|
afterAll(() => {
|
|
|
|
|
/* 在所有测试后运行 */
|
|
|
|
|
})
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
/* 在每次测试之前运行 */
|
|
|
|
|
})
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
/* 每次测试后运行 */
|
|
|
|
|
})
|
|
|
|
|
test('make each pony pink', () => {
|
|
|
|
|
const actual = fn(['Alice', 'Bob', 'Eve'])
|
|
|
|
|
expect(actual).toEqual(['Pink Alice', 'Pink Bob', 'Pink Eve'])
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2022-10-08 03:44:42 +08:00
|
|
|
|
### 聚焦测试
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
describe.only(···)
|
|
|
|
|
|
|
|
|
|
it.only(···)
|
|
|
|
|
// 别名: fit()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
查看: [test.only](https://jestjs.io/docs/api#testonlyname-fn-timeout)
|
|
|
|
|
|
|
|
|
|
### 可选参数
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
| Flag | Description |
|
|
|
|
|
| --------------------- | ---------------------------------------- |
|
|
|
|
|
| `--coverage` | 查看测试覆盖率摘要 |
|
|
|
|
|
| `--detectOpenHandles` | 查看未关闭端口的摘要 |
|
|
|
|
|
| `--runInBand` | 一个接一个地运行所有测试 |
|
|
|
|
|
| `--bail,-b` | 失败跳出测试 |
|
|
|
|
|
|
|
|
|
|
更多参数查看 [Jest CLI Options](https://jestjs.io/docs/next/cli#--bailn)
|
|
|
|
|
|
|
|
|
|
### 跳过测试
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
describe.skip(···)
|
|
|
|
|
|
|
|
|
|
it.skip(···)
|
|
|
|
|
// 别名: xit()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
查看: [test.skip](https://jestjs.io/docs/next/api#testskipname-fn)
|
|
|
|
|
|
|
|
|
|
### 基本测试用例
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect(value).not.toBe(value)
|
|
|
|
|
.toEqual(value)
|
|
|
|
|
.toBeTruthy()
|
|
|
|
|
// Errors 测试
|
|
|
|
|
expect(value).toThrow(error)
|
|
|
|
|
.toThrowErrorMatchingSnapshot()
|
|
|
|
|
```
|
|
|
|
|
|
2022-10-08 23:55:59 +08:00
|
|
|
|
### 快照
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect(value)
|
|
|
|
|
.toMatchSnapshot()
|
|
|
|
|
.toMatchInlineSnapshot()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Errors
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect(value)
|
|
|
|
|
.toThrow(error)
|
|
|
|
|
.toThrowErrorMatchingSnapshot()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Objects
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect(value)
|
|
|
|
|
.toBeInstanceOf(Class)
|
|
|
|
|
.toMatchObject(object)
|
|
|
|
|
.toHaveProperty(keyPath, value)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Objects
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect(value)
|
|
|
|
|
.toContain(item)
|
|
|
|
|
.toContainEqual(item)
|
|
|
|
|
.toHaveLength(number)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Numbers
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect(value)
|
|
|
|
|
.toBeCloseTo(number, numDigits)
|
|
|
|
|
.toBeGreaterThan(number)
|
|
|
|
|
.toBeGreaterThanOrEqual(number)
|
|
|
|
|
.toBeLessThan(number)
|
|
|
|
|
.toBeLessThanOrEqual(number)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Booleans
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect(value)
|
|
|
|
|
.toBeFalsy()
|
|
|
|
|
.toBeNull()
|
|
|
|
|
.toBeTruthy()
|
|
|
|
|
.toBeUndefined()
|
|
|
|
|
.toBeDefined()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Strings
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect(value)
|
|
|
|
|
.toMatch(regexpOrString)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### NaN
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
test('当值为 NaN 时通过', () => {
|
|
|
|
|
expect(NaN).toBeNaN();
|
|
|
|
|
expect(1).not.toBeNaN();
|
|
|
|
|
});
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Others
|
|
|
|
|
<!--rehype:wrap-class=col-span-2-->
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect.extend(matchers)
|
|
|
|
|
expect.any(constructor)
|
|
|
|
|
expect.addSnapshotSerializer(serializer)
|
|
|
|
|
|
|
|
|
|
expect.assertions(1)
|
|
|
|
|
```
|
|
|
|
|
|
2022-09-27 23:09:51 +08:00
|
|
|
|
匹配器
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
### 基本匹配器
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect(42).toBe(42) // 严格相等 (===)
|
|
|
|
|
expect(42).not.toBe(3) // 严格相等 (!==)
|
|
|
|
|
expect([1, 2]).toEqual([1, 2]) // 深度相等
|
|
|
|
|
|
|
|
|
|
// 深度相等
|
|
|
|
|
expect({ a: undefined, b: 2 })
|
|
|
|
|
.toEqual({ b: 2 })
|
|
|
|
|
|
|
|
|
|
// 严格相等 (Jest 23+)
|
|
|
|
|
expect({ a: undefined, b: 2 })
|
|
|
|
|
.not.toStrictEqual({ b: 2 })
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
[Using matchers](http://jestjs.io/docs/en/using-matchers), [matchers docs](https://jestjs.io/docs/en/expect)
|
|
|
|
|
|
|
|
|
|
### 真实性
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 匹配 if 语句视为 true 的任何内容
|
|
|
|
|
// (not false、0、''、null、undefined、NaN)
|
|
|
|
|
expect('foo').toBeTruthy()
|
|
|
|
|
// 匹配 if 语句视为 false 的任何内容
|
|
|
|
|
// (false、0、''、null、undefined、NaN)
|
|
|
|
|
expect('').toBeFalsy()
|
|
|
|
|
// 仅匹配 null
|
|
|
|
|
expect(null).toBeNull()
|
|
|
|
|
// 仅匹配未定义
|
|
|
|
|
expect(undefined).toBeUndefined()
|
|
|
|
|
// toBeUndefined 的反义词
|
|
|
|
|
expect(7).toBeDefined()
|
|
|
|
|
// 匹配真假
|
|
|
|
|
expect(true).toEqual(expect.any(Boolean))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 数字
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 大于
|
|
|
|
|
expect(2).toBeGreaterThan(1)
|
|
|
|
|
// 大于或等于
|
|
|
|
|
expect(1).toBeGreaterThanOrEqual(1)
|
|
|
|
|
// 小于
|
|
|
|
|
expect(1).toBeLessThan(2)
|
|
|
|
|
// 小于或等于
|
|
|
|
|
expect(1).toBeLessThanOrEqual(1)
|
|
|
|
|
// 接近于
|
|
|
|
|
expect(0.2 + 0.1).toBeCloseTo(0.3, 5)
|
|
|
|
|
// 原始值的传递类型
|
|
|
|
|
expect(NaN).toEqual(expect.any(Number))
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 字符串
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 检查字符串是否与正则表达式匹配。
|
|
|
|
|
expect('long string').toMatch('str')
|
|
|
|
|
expect('string').toEqual(expect.any(String))
|
|
|
|
|
expect('coffee').toMatch(/ff/)
|
|
|
|
|
expect('pizza').not.toMatch('coffee')
|
|
|
|
|
expect(['pizza', 'coffee']).toEqual(
|
|
|
|
|
[
|
|
|
|
|
expect.stringContaining('zz'),
|
|
|
|
|
expect.stringMatching(/ff/)
|
|
|
|
|
]
|
|
|
|
|
)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 数组
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect([]).toEqual(expect.any(Array))
|
|
|
|
|
const exampleArray = [
|
|
|
|
|
'Alice', 'Bob', 'Eve'
|
|
|
|
|
]
|
|
|
|
|
expect(exampleArray).toHaveLength(3)
|
|
|
|
|
expect(exampleArray).toContain('Alice')
|
|
|
|
|
expect(exampleArray).toEqual(
|
|
|
|
|
expect.arrayContaining(['Alice', 'Bob'])
|
|
|
|
|
)
|
|
|
|
|
expect([{ a: 1 }, { a: 2 }])
|
|
|
|
|
.toContainEqual({ a: 1 }) // 包含相等
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 对象
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
expect({ a:1 }).toHaveProperty('a')
|
|
|
|
|
expect({ a:1 }).toHaveProperty('a', 1)
|
|
|
|
|
expect({ a: {b:1} }).toHaveProperty('a.b')
|
|
|
|
|
expect({ a:1, b:2 }).toMatchObject({a:1})
|
|
|
|
|
expect({ a:1, b:2 }).toMatchObject({
|
|
|
|
|
a: expect.any(Number),
|
|
|
|
|
b: expect.any(Number),
|
|
|
|
|
})
|
|
|
|
|
expect([{ a: 1 }, { b: 2 }]).toEqual([
|
|
|
|
|
expect.objectContaining(
|
|
|
|
|
{ a: expect.any(Number) }
|
|
|
|
|
),
|
|
|
|
|
expect.anything(),
|
|
|
|
|
])
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 模拟函数
|
2022-09-28 13:35:52 +08:00
|
|
|
|
<!--rehype:wrap-class=row-span-2-->
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// const fn = jest.fn()
|
|
|
|
|
// const fn = jest.fn().mockName('Unicorn') -- 命名为 mock, Jest 22+
|
|
|
|
|
// 函数被调用
|
|
|
|
|
expect(fn).toBeCalled()
|
|
|
|
|
// 函数*未*调用
|
|
|
|
|
expect(fn).not.toBeCalled()
|
|
|
|
|
// 函数只被调用一次
|
|
|
|
|
expect(fn).toHaveBeenCalledTimes(1)
|
|
|
|
|
// 任何执行都带有这些参数
|
|
|
|
|
expect(fn).toBeCalledWith(arg1, arg2)
|
|
|
|
|
// 最后一个执行是用这些参数
|
|
|
|
|
expect(fn).toHaveBeenLastCalledWith(arg1, arg2)
|
|
|
|
|
// 第 N 个执行带有这些参数(Jest 23+)
|
|
|
|
|
expect(fn).toHaveBeenNthCalledWith(callNumber, args)
|
|
|
|
|
// 函数返回没有抛出错误(Jest 23+)
|
|
|
|
|
expect(fn).toHaveReturnedTimes(2)
|
|
|
|
|
// 函数返回一个值(Jest 23+)
|
|
|
|
|
expect(fn).toHaveReturnedWith(value)
|
|
|
|
|
// 最后一个函数调用返回一个值(Jest 23+)
|
|
|
|
|
expect(fn).toHaveLastReturnedWith(value)
|
|
|
|
|
// 第 N 个函数调用返回一个值(Jest 23+)
|
|
|
|
|
expect(fn).toHaveNthReturnedWith(value)
|
|
|
|
|
expect(fn.mock.calls).toEqual([
|
|
|
|
|
['first', 'call', 'args'],
|
|
|
|
|
['second', 'call', 'args'],
|
|
|
|
|
]) // 多次调用
|
|
|
|
|
// fn.mock.calls[0][0] — 第一次调用的第一个参数
|
|
|
|
|
expect(fn.mock.calls[0][0]).toBe(2)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 别名
|
|
|
|
|
|
|
|
|
|
- `toBeCalled` → `toHaveBeenCalled`
|
|
|
|
|
- `toBeCalledWith` → `toHaveBeenCalledWith`
|
|
|
|
|
- `lastCalledWith` → `toHaveBeenLastCalledWith`
|
|
|
|
|
- `nthCalledWith` → `toHaveBeenNthCalledWith`
|
|
|
|
|
- `toReturnTimes` → `toHaveReturnedTimes`
|
|
|
|
|
- `toReturnWith` → `toHaveReturnedWith`
|
|
|
|
|
- `lastReturnedWith` → `toHaveLastReturnedWith`
|
|
|
|
|
- `nthReturnedWith` → `toHaveNthReturnedWith`
|
|
|
|
|
|
|
|
|
|
### 杂项
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 检查对象是否是类的实例。
|
|
|
|
|
expect(new A()).toBeInstanceOf(A)
|
|
|
|
|
|
|
|
|
|
// 检查对象是否是函数的实例。
|
|
|
|
|
expect(() => {}).toEqual(
|
|
|
|
|
expect.any(Function)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 匹配除 null 或 undefined 之外的任何内容
|
|
|
|
|
expect('pizza').toEqual(expect.anything())
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 快照
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 这可确保某个值与最近的快照匹配。
|
|
|
|
|
expect(node).toMatchSnapshot()
|
|
|
|
|
|
|
|
|
|
// Jest 23+
|
|
|
|
|
expect(user).toMatchSnapshot({
|
|
|
|
|
date: expect.any(Date),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 确保值与最近的快照匹配。
|
|
|
|
|
expect(user).toMatchInlineSnapshot()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Promise 匹配器(Jest 20+)
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
test('resolve to lemon', () => {
|
|
|
|
|
// 验证在测试期间是否调用了一定数量的断言。
|
|
|
|
|
expect.assertions(1)
|
|
|
|
|
// 确保添加return语句
|
|
|
|
|
return expect(Promise.resolve('lemon'))
|
|
|
|
|
.resolves.toBe('lemon')
|
|
|
|
|
|
|
|
|
|
return expect(Promise.reject('octopus'))
|
|
|
|
|
.rejects.toBeDefined()
|
|
|
|
|
|
|
|
|
|
return expect(Promise.reject(
|
|
|
|
|
Error('pizza')
|
|
|
|
|
)).rejects.toThrow()
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
或者使用 async/await:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
test('resolve to lemon', async () => {
|
|
|
|
|
expect.assertions(2)
|
|
|
|
|
await expect(Promise.resolve('lemon'))
|
|
|
|
|
.resolves.toBe('lemon')
|
|
|
|
|
|
|
|
|
|
await expect(Promise.resolve('lemon'))
|
|
|
|
|
.resolves.not.toBe('octopus')
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
[resolves 文档](https://jestjs.io/docs/en/expect#resolves)
|
|
|
|
|
|
|
|
|
|
### 例外
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// const fn = () => {
|
|
|
|
|
// throw new Error('Out of cheese!')
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
expect(fn).toThrow()
|
|
|
|
|
expect(fn).toThrow('Out of cheese')
|
|
|
|
|
|
|
|
|
|
// 测试错误消息在某处说“cheese”:这些是等价的
|
|
|
|
|
expect(fn).toThrowError(/cheese/);
|
|
|
|
|
expect(fn).toThrowError('cheese');
|
|
|
|
|
|
|
|
|
|
// 测试准确的错误信息
|
|
|
|
|
expect(fn).toThrowError(
|
|
|
|
|
/^Out of cheese!$/
|
|
|
|
|
);
|
|
|
|
|
expect(fn).toThrowError(
|
|
|
|
|
new Error('Out of cheese!')
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// 测试函数在调用时是否抛出与最新快照匹配的错误。
|
|
|
|
|
expect(fn).toThrowErrorMatchingSnapshot()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 别名
|
|
|
|
|
|
|
|
|
|
- `toThrowError` → `toThrow`
|
|
|
|
|
|
|
|
|
|
异步测试
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
### 实例
|
2022-09-28 13:35:52 +08:00
|
|
|
|
<!--rehype:wrap-class=row-span-2-->
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
在异步测试中指定一些预期的断言是一个很好的做法,所以如果你的断言根本没有被调用,测试将会失败。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
test('async test', () => {
|
|
|
|
|
// 在测试期间恰好调用了三个断言
|
|
|
|
|
expect.assertions(3)
|
|
|
|
|
// 或者 - 在测试期间至少调用一个断言
|
|
|
|
|
expect.hasAssertions()
|
|
|
|
|
// 你的异步测试
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
请注意,您也可以在任何 `describe` 和 `test` 之外对每个文件执行此操作:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
beforeEach(expect.hasAssertions)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这将验证每个测试用例至少存在一个断言。 它还可以与更具体的 `expect.assertions(3)` 声明配合使用。
|
2022-10-08 23:55:59 +08:00
|
|
|
|
请参阅 Jest 文档中的 [更多示例](https://jestjs.io/docs/en/tutorial-async)
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
### async/await
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
test('async test', async () => {
|
|
|
|
|
expect.assertions(1)
|
|
|
|
|
|
|
|
|
|
const result = await runAsyncOperation()
|
|
|
|
|
expect(result).toBe(true)
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### done() 回调
|
2022-09-28 13:35:52 +08:00
|
|
|
|
<!--rehype:wrap-class=row-span-2-->
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
test('async test', (done) => {
|
|
|
|
|
expect.assertions(1)
|
|
|
|
|
|
|
|
|
|
runAsyncOperation()
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
try {
|
2022-10-08 23:55:59 +08:00
|
|
|
|
const res = getAsyncOperatResult()
|
|
|
|
|
expect(res).toBe(true)
|
2022-09-27 23:09:51 +08:00
|
|
|
|
done()
|
|
|
|
|
} catch (err) {
|
|
|
|
|
done.fail(err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2022-10-02 03:23:32 +08:00
|
|
|
|
将断言包装在 `try/catch` 块中,否则 `Jest` 将忽略失败
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
### Promises
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
test('async test', () => {
|
|
|
|
|
expect.assertions(1)
|
|
|
|
|
|
|
|
|
|
return runAsyncOperation().then((result) => {
|
|
|
|
|
expect(result).toBe(true)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2022-10-02 03:23:32 +08:00
|
|
|
|
从你的测试中 _返回_ 一个 `Promise`
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 模拟
|
|
|
|
|
|
|
|
|
|
### 模拟函数
|
2022-09-28 13:35:52 +08:00
|
|
|
|
<!--rehype:wrap-class=row-span-2-->
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
test('call the callback', () => {
|
|
|
|
|
const callback = jest.fn()
|
|
|
|
|
fn(callback)
|
|
|
|
|
expect(callback).toBeCalled()
|
2022-10-08 23:55:59 +08:00
|
|
|
|
expect(callback.mock.calls[0][1].baz)
|
|
|
|
|
.toBe('pizza') // 第一次调用的第二个参数
|
|
|
|
|
|
2022-09-27 23:09:51 +08:00
|
|
|
|
// 匹配第一个和最后一个参数,但忽略第二个参数
|
2022-10-08 23:55:59 +08:00
|
|
|
|
expect(callback)
|
|
|
|
|
.toHaveBeenLastCalledWith(
|
|
|
|
|
'meal',
|
|
|
|
|
expect.anything(),
|
|
|
|
|
'margarita'
|
|
|
|
|
)
|
2022-09-27 23:09:51 +08:00
|
|
|
|
})
|
|
|
|
|
```
|
2022-10-08 23:55:59 +08:00
|
|
|
|
<!--rehype:className=wrap-text -->
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
您还可以使用快照:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
test('call the callback', () => {
|
|
|
|
|
// mockName 在 Jest 22+ 中可用
|
2022-10-08 23:55:59 +08:00
|
|
|
|
const callback = jest.fn()
|
|
|
|
|
.mockName('Unicorn')
|
|
|
|
|
|
2022-09-27 23:09:51 +08:00
|
|
|
|
fn(callback)
|
|
|
|
|
expect(callback).toMatchSnapshot()
|
|
|
|
|
// ->
|
|
|
|
|
// [MockFunction Unicorn] {
|
|
|
|
|
// "calls": Array [
|
|
|
|
|
// ...
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
并将实现传递给 `jest.fn` 函数:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const callback = jest.fn(() => true)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
[模拟函数文档](https://jestjs.io/docs/en/mock-function-api)
|
|
|
|
|
|
|
|
|
|
### 返回、解析和拒绝值
|
2022-09-28 13:35:52 +08:00
|
|
|
|
<!--rehype:wrap-class=row-span-2-->
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
您的模拟可以返回值:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const callback
|
|
|
|
|
= jest.fn().mockReturnValue(true)
|
2022-10-08 23:55:59 +08:00
|
|
|
|
|
2022-09-27 23:09:51 +08:00
|
|
|
|
const callbackOnce
|
|
|
|
|
= jest.fn().mockReturnValueOnce(true)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
或解析值:
|
|
|
|
|
|
|
|
|
|
```js
|
2022-10-08 23:55:59 +08:00
|
|
|
|
const promise
|
2022-09-27 23:09:51 +08:00
|
|
|
|
= jest.fn().mockResolvedValue(true)
|
2022-10-08 23:55:59 +08:00
|
|
|
|
|
|
|
|
|
const promiseOnce
|
2022-09-27 23:09:51 +08:00
|
|
|
|
= jest.fn().mockResolvedValueOnce(true)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
他们甚至可以拒绝值:
|
|
|
|
|
|
|
|
|
|
```js
|
2022-10-08 23:55:59 +08:00
|
|
|
|
const failedPromise =
|
|
|
|
|
jest.fn().mockRejectedValue('Error')
|
|
|
|
|
|
|
|
|
|
const failedPromiseOnce =
|
|
|
|
|
jest.fn().mockRejectedValueOnce('Error')
|
2022-09-27 23:09:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
你甚至可以结合这些:
|
|
|
|
|
|
|
|
|
|
```js
|
2022-10-08 23:55:59 +08:00
|
|
|
|
const callback = jest.fn()
|
|
|
|
|
.mockReturnValueOnce(false)
|
|
|
|
|
.mockReturnValue(true)
|
2022-09-27 23:09:51 +08:00
|
|
|
|
// ->
|
|
|
|
|
// call 1: false
|
|
|
|
|
// call 2+: true
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 使用 `jest.mock` 方法模拟模块
|
|
|
|
|
|
|
|
|
|
```js
|
2022-10-08 23:55:59 +08:00
|
|
|
|
// 原来的 lodash/memoize 应该存在
|
|
|
|
|
jest.mock(
|
|
|
|
|
'lodash/memoize',
|
|
|
|
|
() => (a) => a
|
|
|
|
|
)
|
|
|
|
|
// 不需要原始的 lodash/memoize
|
|
|
|
|
jest.mock(
|
|
|
|
|
'lodash/memoize',
|
|
|
|
|
() => (a) => a,
|
|
|
|
|
{ virtual: true }
|
|
|
|
|
)
|
2022-09-27 23:09:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
2022-10-08 23:55:59 +08:00
|
|
|
|
注意:当使用 `babel-jest` 时,对 [`jest.mock`](https://jestjs.io/docs/en/jest-object#jestmockmodulename-factory-options) 的调用将自动提升到代码块的顶部。 如果您想明确避免这种行为,请使用 `jest.doMock`。
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
### 使用模拟文件模拟模块
|
|
|
|
|
|
|
|
|
|
创建一个类似 `__mocks__/lodash/memoize.js` 的文件:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
module.exports = (a) => a
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
添加到您的测试中:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
jest.mock('lodash/memoize')
|
|
|
|
|
```
|
|
|
|
|
|
2022-10-08 23:55:59 +08:00
|
|
|
|
注意:当使用 `babel-jest` 时,对 `jest.mock` 的调用将自动提升到代码块的顶部。 如果您想明确避免这种行为,请使用 `jest.doMock`。[手动模拟文档](https://jestjs.io/docs/en/manual-mocks)
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
2022-10-08 23:55:59 +08:00
|
|
|
|
### 模拟 getters 和 setters
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
```js
|
2022-10-08 23:55:59 +08:00
|
|
|
|
const getTitle = jest.fn(() => 'pizza')
|
|
|
|
|
const setTitle = jest.fn()
|
|
|
|
|
const location = {}
|
|
|
|
|
Object.defineProperty(location, 'title', {
|
|
|
|
|
get: getTitle,
|
|
|
|
|
set: setTitle,
|
|
|
|
|
})
|
2022-09-27 23:09:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 模拟 getter 和 setter (Jest 22.1.0+)
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const location = {}
|
|
|
|
|
const getTitle = jest
|
|
|
|
|
.spyOn(location, 'title', 'get')
|
|
|
|
|
.mockImplementation(() => 'pizza')
|
|
|
|
|
const setTitle = jest
|
|
|
|
|
.spyOn(location, 'title', 'set')
|
|
|
|
|
.mockImplementation(() => {})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 定时器模拟
|
2022-09-28 13:35:52 +08:00
|
|
|
|
<!--rehype:wrap-class=row-span-3-->
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
为使用本机计时器函数(`setTimeout`、`setInterval`、`clearTimeout`、`clearInterval`)的代码编写同步测试。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 启用假计时器
|
|
|
|
|
jest.useFakeTimers()
|
|
|
|
|
test('kill the time', () => {
|
|
|
|
|
const callback = jest.fn()
|
2022-10-08 23:55:59 +08:00
|
|
|
|
// 运行使用 setTimeout或setInterval 的代码
|
|
|
|
|
const actual
|
|
|
|
|
= someFunctionThatUseTimers(callback)
|
2022-09-27 23:09:51 +08:00
|
|
|
|
// 快进直到所有定时器都执行完毕
|
|
|
|
|
jest.runAllTimers()
|
|
|
|
|
// 同步检查结果
|
|
|
|
|
expect(callback).toHaveBeenCalledTimes(1)
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
或者使用 [advanceTimersByTime()](https://jestjs.io/docs/en/timer-mocks#advance-timers-by-time) 按时间调整计时器:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 启用假计时器
|
|
|
|
|
jest.useFakeTimers()
|
|
|
|
|
test('kill the time', () => {
|
|
|
|
|
const callback = jest.fn()
|
2022-10-08 23:55:59 +08:00
|
|
|
|
// 运行使用 setTimeout或setInterval 的代码
|
|
|
|
|
const actual
|
|
|
|
|
= someFunctionThatUseTimers(callback)
|
2022-09-27 23:09:51 +08:00
|
|
|
|
// 快进 250 毫秒
|
|
|
|
|
jest.advanceTimersByTime(250)
|
|
|
|
|
// 同步检查结果
|
|
|
|
|
expect(callback).toHaveBeenCalledTimes(1)
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2022-10-08 23:55:59 +08:00
|
|
|
|
> 对于特殊情况,请使用 [jest.runOnlyPendingTimers()](https://jestjs.io/docs/en/timer-mocks#run-pending-timers)。
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
**注意:** 您应该在测试用例中调用 `jest.useFakeTimers()` 以使用其他假计时器方法。
|
|
|
|
|
|
2022-10-08 23:55:59 +08:00
|
|
|
|
### 模拟对象方法
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
```js
|
2022-10-08 23:55:59 +08:00
|
|
|
|
const spy = jest.spyOn(console, 'log')
|
|
|
|
|
.mockImplementation(() => {})
|
|
|
|
|
|
|
|
|
|
expect(console.log.mock.calls)
|
|
|
|
|
.toEqual([['dope'], ['nope']])
|
|
|
|
|
spy.mockRestore()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const spy = jest.spyOn(ajax, 'request')
|
|
|
|
|
.mockImplementation(
|
|
|
|
|
() => Promise.resolve({success: true})
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(spy).toHaveBeenCalled()
|
|
|
|
|
spy.mockRestore()
|
2022-09-27 23:09:51 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 清除和恢复模拟
|
2022-09-28 13:35:52 +08:00
|
|
|
|
<!--rehype:wrap-class=row-span-2-->
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
对于一个模拟
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 清除模拟使用日期
|
|
|
|
|
// (fn.mock.calls、fn.mock.instances)
|
|
|
|
|
fn.mockClear()
|
2022-10-08 23:55:59 +08:00
|
|
|
|
|
2022-09-27 23:09:51 +08:00
|
|
|
|
// 清除并删除任何模拟的返回值或实现
|
|
|
|
|
fn.mockReset()
|
2022-10-08 23:55:59 +08:00
|
|
|
|
|
2022-09-27 23:09:51 +08:00
|
|
|
|
// 重置并恢复初始实现
|
|
|
|
|
fn.mockRestore()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> 注意:`mockRestore` 仅适用于由`jest.spyOn` 创建的模拟。对于所有模拟:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
// 清除所有 mock 的
|
|
|
|
|
// mock.calls、mock.instances、
|
|
|
|
|
// mock.contexts 和 mock.results 属性。
|
|
|
|
|
jest.clearAllMocks()
|
|
|
|
|
// 重置所有模拟的状态。
|
|
|
|
|
jest.resetAllMocks()
|
|
|
|
|
// 将所有模拟恢复到其原始值。
|
|
|
|
|
jest.restoreAllMocks()
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 使用模拟时访问原始模块
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
jest.mock('fs')
|
|
|
|
|
// 模拟模块
|
|
|
|
|
const fs = require('fs')
|
|
|
|
|
// 原始模块
|
|
|
|
|
const fs = require.requireActual('fs')
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
数据驱动测试(Jest 23+)
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
### 使用不同的数据运行相同的测试
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
test.each([
|
|
|
|
|
[1, 1, 2],
|
|
|
|
|
[1, 2, 3],
|
|
|
|
|
[2, 1, 3],
|
|
|
|
|
])('.add(%s, %s)', (a, b, expected) => {
|
|
|
|
|
expect(a + b).toBe(expected)
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 使用模板文字相同
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
test.each`
|
|
|
|
|
a | b | expected
|
|
|
|
|
${1} | ${1} | ${2}
|
|
|
|
|
${1} | ${2} | ${3}
|
|
|
|
|
${2} | ${1} | ${3}
|
|
|
|
|
`('returns $expected when $a is added $b', ({ a, b, expected }) => {
|
|
|
|
|
expect(a + b).toBe(expected)
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 或在“describe”级别
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
describe.each([
|
|
|
|
|
['mobile'], ['tablet'], ['desktop']
|
|
|
|
|
])('checkout flow on %s', (viewport) => {
|
|
|
|
|
test('displays success page', () => {
|
|
|
|
|
//
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2022-10-02 03:23:32 +08:00
|
|
|
|
[describe.each()](https://jestjs.io/docs/en/api#describeeachtablename-fn-timeout) 文档、[test.each()](https://jestjs.io/docs/en/api#testeachtablename-fn-timeout) 文档
|
2022-09-27 23:09:51 +08:00
|
|
|
|
|
|
|
|
|
跳过测试
|
|
|
|
|
----
|
|
|
|
|
<!--rehype:body-class=cols-2-->
|
|
|
|
|
|
|
|
|
|
### 不要运行这些测试
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
describe.skip('makePoniesPink'...
|
|
|
|
|
tests.skip('make each pony pink'...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 仅运行以下测试
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
describe.only('makePoniesPink'...
|
|
|
|
|
tests.only('make each pony pink'...
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
测试有副作用的模块
|
|
|
|
|
----
|
|
|
|
|
<!--rehype:body-class=cols-1-->
|
|
|
|
|
|
|
|
|
|
### 实例
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const modulePath = '../module-to-test'
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
jest.resetModules()
|
|
|
|
|
})
|
|
|
|
|
test('第一次测试', () => {
|
|
|
|
|
// 准备第一次测试的条件
|
|
|
|
|
const result = require(modulePath)
|
|
|
|
|
expect(result).toMatchSnapshot()
|
|
|
|
|
})
|
|
|
|
|
test('第二个文本', () => {
|
|
|
|
|
// 准备第二次测试的条件
|
|
|
|
|
const fn = () => require(modulePath)
|
|
|
|
|
expect(fn).toThrow()
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
2022-10-02 03:23:32 +08:00
|
|
|
|
`Node.js` 和 `Jest` 会缓存你需要的模块。 要测试具有副作用的模块,您需要在测试之间重置模块注册表
|
2022-09-28 00:52:28 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
另见
|
|
|
|
|
----
|
|
|
|
|
|
|
|
|
|
- [Jest cheat sheet](https://github.com/sapegin/jest-cheat-sheet) _(github.com)_
|