在写测试时,我经常需要提前准备好测试代码,比如如果我想测试下面这个模块:
// testMe.ts
import otherModule from 'other-module'
// 读取其它模块的值
const id = otherModule.id
// 读取环境变量
const key = process.env.KEY
// 读取全局变量
const ua = navigator.userAgent
export function testMe() {
return id + ':' + key + ':' + ua
}
假设我的测试用例是这么写的:
// testMe.test.ts
import { testMe } from './testMe'
test('s', () => {
expect(testMe()).toBe('a:b:c')
})
如果就这样运行,那么 testMe() 的结果会是 undefined:undefined:undefined,怎样才能确保这个值变成我想要的 a:b:c?
对于模块,可以用 jest.mock
jest 会将 jest.mock 提升至所有模块 import 之前运行:
// testMe.test.ts
import { testMe } from './testMe'
jest.mock('other-module', () => ({ id: 'a' })) // 即使它写在 import testMe 之后,实际上它会在 import 前运行
test('s', () => {
expect(testMe()).toBe('a:b:c')
})
对于环境变量 / 全局变量,可以用 setupFiles
利用 setupFiles 提前准备好即可:
// jest-setup.js
process.env.KEY = 'b'
global.navigator = { userAgent: 'c' }
直接把代码写在 import 前是不行的。
// 虽然这两行代码写在 import testMe 前面,但实际上 testMe.ts 里的代码会先运行,因为 babel 会将 import 全提升到顶部。
// `jest.mock()` 之所以能提升到 import 前面,是因为 jest 自己开发了一个 babel 插件
// https://www.npmjs.com/package/babel-plugin-jest-hoist
process.env.KEY = 'b'
global.navigator = { userAgent: 'c' }
import { testMe } from './testMe'
但 setupFiles 对所有测试文件都会效果。如果只想让它对部分测试文件生效,就要在 jest.config.js 里用到 projects:
// jest.config.js
module.exports = {
projects: [
{
testMatch: ['testMe.test.ts'],
setupFiles: ['jest-setup.js']
}
]
}
但单独为一个 test 文件写 projects 配置比较麻烦。如果不想用 projects,还有一个办法。
前面说到 import 会被提升到代码前面,但只要我们的代码也放在一个单独的模块里,然后从 test 文件里 import 就行了:
import './testMe-prepare' // 这个文件里包含设置全局变量的代码
import { testMe } from './testMe'