# 把多个导出的文件收口也有利于单元测试
比如下面的文件
import { CodeBindLoginHandler } from './strategy/code-bind';
import { CodeScanLoginHandler } from './strategy/code-scan';
import { DefaultLoginHandler } from './strategy/default-login';
import { QQInputLoginHandler } from './strategy/qq-input';
import { QQOnlyLoginHandler } from './strategy/qq-only';
import { QQPCLoginHandler } from './strategy/qq-pc';
import { WXOnlyLoginHandler } from './strategy/wx-only';
import { WXPCLoginHandler } from './strategy/wx-pc';
单元测试 mock时,需要mock多个策略:
jest.mock('src/common/login/login-web/handle-login/strategy/code-bind', () => ({
__esModule: true,
CodeBindLoginHandler() {},
}));
jest.mock('src/common/login/login-web/handle-login/strategy/code-scan', () => ({
__esModule: true,
CodeScanLoginHandler() {},
}));
// 省略n个...
如果改成这样,只需要mock一个模块即可:
import {
CodeBindLoginHandler,
CodeScanLoginHandler,
DefaultLoginHandler,
QQInputLoginHandler,
QQOnlyLoginHandler,
QQPCLoginHandler,
WXOnlyLoginHandler,
WXPCLoginHandler,
} from './strategy';
jest.mock('src/common/login/login-web/handle-login/strategy', () => ({
__esModule: true,
CodeBindLoginHandler() {},
CodeScanLoginHandler() {},
DefaultLoginHandler() {},
QQInputLoginHandler() {},
QQOnlyLoginHandler() {},
QQPCLoginHandler() {},
WXOnlyLoginHandler() {},
WXPCLoginHandler() {},
}));
# 另一种mock方法
比较适合引入的外部模块被多次使用:
import { getUrlPara } from 'tools/url/url';
import { getExtPlatParams } from 'tools/url/ext-plat-params';
jest.mock('tools/url/url');
test('return empty string', () => {
getUrlPara.mockReturnValue('');
expect(getExtPlatParams()).toBe('');
});
test('has search', () => {
window.location.search = '?name=mike';
getUrlPara.mockReturnValue('bindcode');
expect(getExtPlatParams()).toBe('?name=mike');
});
# mock构造函数
下面这样写是不行的:
jest.mock('src/common/login/login-web/handle-login/strategy', () => ({
__esModule: true,
QQInputLoginHandler: jest.fn().mockImplementation(() => {
return { handle: mockQQInputFn };
}),
}));
可以这样写:
jest.mock('src/common/login/login-web/handle-login/strategy', () => ({
__esModule: true,
QQInputLoginHandler: jest.fn(() => {
return { handle: mockQQInputFn };
}),
}));
# 获取mock函数的参数
expect(mockFn.mock.calls[0][0]).toBe('ORIGIN_PATHNAME#/');
# toMatch
toMatch 正则匹配部分字符串,当只有一部分字符串确认时有用。
return expect(getLoginUrlList()).rejects.toMatch('MOCK_ERROR');
# 单测用例从入口引入,可以提升覆盖率
但是要注意其他文件的副作用
比如有个收口文件index.ts
如下:
export { handleLogin } from './handle-login';
export { logout } from './logout';
export { checkLogin } from './check-login';
export { getLoginUrlList } from './login-url-list';
export { showMobileLoginDialog } from './show-mobile-login-dialog';
export { showWXPCLoginDialog } from './show-wx-pc-login-dialog';
测试 handleLogin
方法时,可以从此文件引入:
import { handleLogin } from 'src/common/login/index';
而不是:
import { handleLogin } from 'src/common/login/login-web/handle-login';
# setupFilesAfterEnv
如果想在每个文件beforeAll中执行同样的操作,可以在jest.config.js
中设置setupFilesAfterEnv
,指向一个文件数组,比如:
// jest.config.js
module.exports = {
setupFilesAfterEnv: ['<rootDir>/tests/setupTests.ts']
}
// tests/setupTests.ts
beforeEach(() => {
console.log('before each')
})
afterEach(() => {
console.log('after each')
})
实际应用场景比如,jest环境中无法设置location.href
,因为默认是不可变的,可以这样设置:
// tests/setupTests.ts
function mockLocation() {
Object.defineProperty(window, 'location', {
value: {
href: 'http://localhost/',
search: '',
hash: '',
origin: '',
host: '',
replace(url) {
this.href = url;
},
},
configurable: true,
writable: true,
});
}
beforeAll(() => {
mockLocation();
});
这个做对 location 上的属性做 mock 时,就轻松很多。
# __mock__
对于没有安装的依赖,可以在项目根目录下,也就是和node_modules
同一层,新建__mock__
文件夹
这样的好处,不用安装额外的依赖,比如lodash-es
。