Jest 是一款优雅、简洁的 JavaScript 测试框架。
Jest 是一款优雅、简洁的 JavaScript 测试框架。
无需配置
大多数 JS 项目中即装即用,无需配置优秀接口
从it
到expect
- Jest 将工具包整合在一处。文档齐全、不断维护,非常不错。隔离的
并行进行测试,发挥每一丝算力。快照
轻松编写持续追踪大型对象的测试,并在测试旁或代码内显示实时快照。代码覆盖
无需其他操作,您仅需添加--coverage
参数来生成代码覆盖率报告。
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'])
})
})
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
请参阅 Jest 文档中的 更多示例。
在异步测试中指定一些预期的断言是一个很好的做法,所以如果你的断言根本没有被调用,测试将会失败。
test('async test', () => {
// 在测试期间恰好调用了三个断言
expect.assertions(3)
// 或者 - 在测试期间至少调用一个断言
expect.hasAssertions()
// 你的异步测试
})
请注意,您也可以在任何 describe
和 test
之外对每个文件执行此操作:
beforeEach(expect.hasAssertions)
这将验证每个测试用例至少存在一个断言。 它还可以与更具体的 expect.assertions(3)
声明配合使用。
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 result = getAsyncOperationResult()
expect(result).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
jest.mock('lodash/memoize', () => (a) => a) // The original lodash/memoize should exist
jest.mock('lodash/memoize', () => (a) => a, { virtual: true }) // The original lodash/memoize isn’t required
注意:当使用
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 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()
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 getTitle = jest.fn(() => 'pizza')
const setTitle = jest.fn()
const location = {}
Object.defineProperty(location, 'title', {
get: getTitle,
set: setTitle,
})
对于一个模拟
// 清除模拟使用日期
// (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)