Jest 是一款优雅、简洁的 JavaScript 测试框架,这里介绍了它的入门和 一些 API 的索引。
Jest v29 是一款优雅、简洁的 JavaScript 测试框架。
无需配置
大多数 JS 项目中即装即用,无需配置优秀接口
从it
到expect
- Jest 将工具包整合在一处。文档齐全、不断维护,非常不错。隔离的
并行进行测试,发挥每一丝算力。快照
轻松编写持续追踪大型对象的测试,并在测试旁或代码内显示实时快照。代码覆盖
无需其他操作,您仅需添加--coverage
参数来生成代码覆盖率报告。
npm install --save-dev jest
Add to package.json
"scripts": {
"test": "jest"
}
运行你的测试
npm test -- --watch
查看: Getting started
describe('My work', () => {
test('works', () => {
expect(2).toEqual(2)
})
})
describe('My work', () => {
it('works', () => { // `it`是`test`的别名
···
})
})
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'])
})
})
describe.only(···)
it.only(···)
// 别名: fit()
查看: test.only
Flag | Description |
---|---|
--coverage |
查看测试覆盖率摘要 |
--detectOpenHandles |
查看未关闭端口的摘要 |
--runInBand |
一个接一个地运行所有测试 |
--bail,-b |
失败跳出测试 |
更多参数查看 Jest CLI Options
describe.skip(···)
it.skip(···)
// 别名: xit()
查看: test.skip
expect(value).not.toBe(value)
.toEqual(value)
.toBeTruthy()
// Errors 测试
expect(value).toThrow(error)
.toThrowErrorMatchingSnapshot()
expect(value)
.toMatchSnapshot()
.toMatchInlineSnapshot()
expect(value)
.toThrow(error)
.toThrowErrorMatchingSnapshot()
expect(value)
.toBeInstanceOf(Class)
.toMatchObject(object)
.toHaveProperty(keyPath, value)
expect(value)
.toContain(item)
.toContainEqual(item)
.toHaveLength(number)
expect(value)
.toBeCloseTo(number, numDigits)
.toBeGreaterThan(number)
.toBeGreaterThanOrEqual(number)
.toBeLessThan(number)
.toBeLessThanOrEqual(number)
expect(value)
.toBeFalsy()
.toBeNull()
.toBeTruthy()
.toBeUndefined()
.toBeDefined()
expect(value)
.toMatch(regexpOrString)
test('当值为 NaN 时通过', () => {
expect(NaN).toBeNaN();
expect(1).not.toBeNaN();
});
expect.extend(matchers)
expect.any(constructor)
expect.addSnapshotSerializer(serializer)
expect.assertions(1)
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 })
// 匹配 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))
// 大于
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))
// 检查字符串是否与正则表达式匹配。
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/)
]
)
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 }) // 包含相等
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(),
])
// 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
// 检查对象是否是类的实例。
expect(new A()).toBeInstanceOf(A)
// 检查对象是否是函数的实例。
expect(() => {}).toEqual(
expect.any(Function)
)
// 匹配除 null 或 undefined 之外的任何内容
expect('pizza').toEqual(expect.anything())
// 这可确保某个值与最近的快照匹配。
expect(node).toMatchSnapshot()
// Jest 23+
expect(user).toMatchSnapshot({
date: expect.any(Date),
})
// 确保值与最近的快照匹配。
expect(user).toMatchInlineSnapshot()
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:
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')
})
// 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
在异步测试中指定一些预期的断言是一个很好的做法,所以如果你的断言根本没有被调用,测试将会失败。
test('async test', () => {
// 在测试期间恰好调用了三个断言
expect.assertions(3)
// 或者 - 在测试期间至少调用一个断言
expect.hasAssertions()
// 你的异步测试
})
请注意,您也可以在任何 describe
和 test
之外对每个文件执行此操作:
beforeEach(expect.hasAssertions)
这将验证每个测试用例至少存在一个断言。 它还可以与更具体的 expect.assertions(3)
声明配合使用。
请参阅 Jest 文档中的 更多示例
test('async test', async () => {
expect.assertions(1)
const result = await runAsyncOperation()
expect(result).toBe(true)
})
test('async test', (done) => {
expect.assertions(1)
runAsyncOperation()
setTimeout(() => {
try {
const res = getAsyncOperatResult()
expect(res).toBe(true)
done()
} catch (err) {
done.fail(err)
}
})
})
将断言包装在 try/catch
块中,否则 Jest
将忽略失败
test('async test', () => {
expect.assertions(1)
return runAsyncOperation().then((result) => {
expect(result).toBe(true)
})
})
从你的测试中 返回 一个 Promise
test('call the callback', () => {
const callback = jest.fn()
fn(callback)
expect(callback).toBeCalled()
expect(callback.mock.calls[0][1].baz)
.toBe('pizza') // 第一次调用的第二个参数
// 匹配第一个和最后一个参数,但忽略第二个参数
expect(callback)
.toHaveBeenLastCalledWith(
'meal',
expect.anything(),
'margarita'
)
})
您还可以使用快照:
test('call the callback', () => {
// mockName 在 Jest 22+ 中可用
const callback = jest.fn()
.mockName('Unicorn')
fn(callback)
expect(callback).toMatchSnapshot()
// ->
// [MockFunction Unicorn] {
// "calls": Array [
// ...
})
并将实现传递给 jest.fn
函数:
const callback = jest.fn(() => true)
您的模拟可以返回值:
const callback
= jest.fn().mockReturnValue(true)
const callbackOnce
= jest.fn().mockReturnValueOnce(true)
或解析值:
const promise
= jest.fn().mockResolvedValue(true)
const promiseOnce
= jest.fn().mockResolvedValueOnce(true)
他们甚至可以拒绝值:
const failedPromise =
jest.fn().mockRejectedValue('Error')
const failedPromiseOnce =
jest.fn().mockRejectedValueOnce('Error')
你甚至可以结合这些:
const callback = jest.fn()
.mockReturnValueOnce(false)
.mockReturnValue(true)
// ->
// call 1: false
// call 2+: true
// 原来的 lodash/memoize 应该存在
jest.mock(
'lodash/memoize',
() => (a) => a
)
// 不需要原始的 lodash/memoize
jest.mock(
'lodash/memoize',
() => (a) => a,
{ virtual: true }
)
注意:当使用 babel-jest
时,对 jest.mock
的调用将自动提升到代码块的顶部。 如果您想明确避免这种行为,请使用 jest.doMock
。
创建一个类似 __mocks__/lodash/memoize.js
的文件:
module.exports = (a) => a
添加到您的测试中:
jest.mock('lodash/memoize')
注意:当使用 babel-jest
时,对 jest.mock
的调用将自动提升到代码块的顶部。 如果您想明确避免这种行为,请使用 jest.doMock
。手动模拟文档
const getTitle = jest.fn(() => 'pizza')
const setTitle = jest.fn()
const location = {}
Object.defineProperty(location, 'title', {
get: getTitle,
set: setTitle,
})
const location = {}
const getTitle = jest
.spyOn(location, 'title', 'get')
.mockImplementation(() => 'pizza')
const setTitle = jest
.spyOn(location, 'title', 'set')
.mockImplementation(() => {})
为使用本机计时器函数(setTimeout
、setInterval
、clearTimeout
、clearInterval
)的代码编写同步测试。
// 启用假计时器
jest.useFakeTimers()
test('kill the time', () => {
const callback = jest.fn()
// 运行使用 setTimeout或setInterval 的代码
const actual
= someFunctionThatUseTimers(callback)
// 快进直到所有定时器都执行完毕
jest.runAllTimers()
// 同步检查结果
expect(callback).toHaveBeenCalledTimes(1)
})
或者使用 advanceTimersByTime() 按时间调整计时器:
// 启用假计时器
jest.useFakeTimers()
test('kill the time', () => {
const callback = jest.fn()
// 运行使用 setTimeout或setInterval 的代码
const actual
= someFunctionThatUseTimers(callback)
// 快进 250 毫秒
jest.advanceTimersByTime(250)
// 同步检查结果
expect(callback).toHaveBeenCalledTimes(1)
})
对于特殊情况,请使用 jest.runOnlyPendingTimers()。
注意: 您应该在测试用例中调用 jest.useFakeTimers()
以使用其他假计时器方法。
const spy = jest.spyOn(console, 'log')
.mockImplementation(() => {})
expect(console.log.mock.calls)
.toEqual([['dope'], ['nope']])
spy.mockRestore()
const spy = jest.spyOn(ajax, 'request')
.mockImplementation(
() => Promise.resolve({success: true})
)
expect(spy).toHaveBeenCalled()
spy.mockRestore()
对于一个模拟
// 清除模拟使用日期
// (fn.mock.calls、fn.mock.instances)
fn.mockClear()
// 清除并删除任何模拟的返回值或实现
fn.mockReset()
// 重置并恢复初始实现
fn.mockRestore()
注意:
mockRestore
仅适用于由jest.spyOn
创建的模拟。对于所有模拟:
// 清除所有 mock 的
// mock.calls、mock.instances、
// mock.contexts 和 mock.results 属性。
jest.clearAllMocks()
// 重置所有模拟的状态。
jest.resetAllMocks()
// 将所有模拟恢复到其原始值。
jest.restoreAllMocks()
jest.mock('fs')
// 模拟模块
const fs = require('fs')
// 原始模块
const fs = require.requireActual('fs')
test.each([
[1, 1, 2],
[1, 2, 3],
[2, 1, 3],
])('.add(%s, %s)', (a, b, expected) => {
expect(a + b).toBe(expected)
})
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.each([
['mobile'], ['tablet'], ['desktop']
])('checkout flow on %s', (viewport) => {
test('displays success page', () => {
//
})
})
describe.each() 文档、test.each() 文档
describe.skip('makePoniesPink'...
tests.skip('make each pony pink'...
describe.only('makePoniesPink'...
tests.only('make each pony pink'...
const modulePath = '../module-to-test'
afterEach(() => {
jest.resetModules()
})
test('第一次测试', () => {
// 准备第一次测试的条件
const result = require(modulePath)
expect(result).toMatchSnapshot()
})
test('第二个文本', () => {
// 准备第二次测试的条件
const fn = () => require(modulePath)
expect(fn).toThrow()
})
Node.js
和 Jest
会缓存你需要的模块。 要测试具有副作用的模块,您需要在测试之间重置模块注册表
- Jest cheat sheet (github.com)