我只想在所有测试用例之前运行一些东西。因此,我创建了一个全局函数,并在 jest 配置中指定了 globalSetup 字段:
globalSetup: path.resolve(srcPath, 'TestUtils', 'globalSetup.ts'),
但是,在 globalSetup 中,我使用了一些别名 @,而 Jest 抱怨它找不到它们。
别名整理完毕后,如何运行 globalSetup?
我的Jest配置如下:
module.exports = {
rootDir: rootPath,
coveragePathIgnorePatterns: ['/node_modules/'],
preset: 'ts-jest',
setupFiles: [path.resolve(__dirname, 'env.testing.ts')],
setupFilesAfterEnv: [path.resolve(srcPath, 'TestUtils', 'testSetup.ts')],
globalSetup: path.resolve(srcPath, 'TestUtils', 'globalSetup.ts'),
globals: {},
testEnvironment: 'node',
moduleFileExtensions: ['js', 'ts', 'json'],
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' })
};
当我在每次测试之前运行 testSetup 时,它可以正常运行别名,但是 globalSetup 不会发生这种情况。
有什么线索我能做什么吗?
我通过在全局设置文件顶部包含
tsconfig-paths/register
成功完成了这项工作:
// myGlobalSetupFile.ts
import 'tsconfig-paths/register';
import { Thing } from './place-with-aliases';
export default async () => {
await Thing.doGlobalSetup();
}
您必须确保您的项目中安装了
tsconfig-paths
。
不幸的是,根据此问题的评论,我发现没有解决方案: https://github.com/facebook/jest/issues/6048
总结是 globalSetup 在 Jest 生态系统之外运行,因此它不会识别别名等。
有多种解决方法,例如,如果您的 npm run test 命令如下所示:
"test": "jest --config config/jest.config.js --detectOpenHandles --forceExit"
然后你可以这样做:
"test": "node whateverBeforeJest.js && jest --config config/jest.config.js --detectOpenHandles --forceExit"
Javier 是对的,globalSetup 在 Jest 生态系统之外运行,并且不会识别别名。
但是我这样解决了问题:
文件夹结构:
src
└── __tests__
└── client
└── server
└── tsconfig.json
└── _config
└── jest.config.ts
└── jest.global.setup.ts
└── jest.global.teardown.ts
└── controllers
└── public.test.ts
└── ...more
└── client
└── ...more
└── server
└── controllers
└── ...more
└── tsconfig-base.json
package.json
package.json
{
...
"dependencies": {
...
},
"devDependencies": {
"supertest": "^6.3.4",
"ts-jest": "^29.1.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.4.2",
...
},
"_moduleAliases": {
"@/controllers": "bin/controllers",
"@/enums": "bin/enums",
"@/exceptions": "bin/exceptions",
"@/interfaces": "bin/interfaces"
},
...
}
tsconfig-base.json
{
"compilerOptions": {
"target": "es2020",
"types": ["node", "jest"],
"composite": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"sourceMap": true,
"strict": true,
"alwaysStrict": true,
"noImplicitAny": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"removeComments": true,
//"traceResolution": true,
},
"include": [
],
"exclude": [
"**/node_modules/*",
"bin",
"public"
]
}
{
"extends": "../../tsconfig-base.json",
"compilerOptions": {
"module": "NodeNext",
"baseUrl": ".",
"rootDir": "../..",
"outDir": "../../../bintests",
"paths": {
"@/controllers/*": ["../../server/controllers/*"],
"@/enums/*": ["../../server/enums/*"],
"@/exceptions/*": ["../../server/exceptions/*"],
"@/interfaces/*": ["../../server/interfaces/*"]
},
},
"include": [
"**/next-env.d.ts",
"**/*.ts",
"../../server/**/*.ts"
],
"exclude": [
"**/node_modules/*",
"bin",
"public"
]
}
jest.config.ts
import _path from 'path';
import type { Config } from '@jest/types';
import { pathsToModuleNameMapper } from 'ts-jest';
const testsSrc: string = _path.resolve(__dirname, '../..');
const serverSrc: string = _path.resolve(__dirname, '../../../server');
export const aliases: any = {
'@/controllers/*': [`${serverSrc}/controllers/*`],
'@/enums/*': [`${serverSrc}/enums/*`],
'@/exceptions/*': [`${serverSrc}/exceptions/*`],
'@/interfaces/*': [`${serverSrc}/interfaces/*`],
};
const config: Config.InitialOptions = {
rootDir: `${testsSrc}/server`,
roots: ['<rootDir>'],
testMatch: [
//'**/__tests__/**/*.+(ts|tsx|js)',
'**/?(*.)+(spec|test).+(ts|tsx|js)',
],
transform: {
'^.+\\.(ts|tsx)$': 'ts-jest',
},
preset: 'ts-jest',
//'ts-jest/presets/default-esm',
testEnvironment: 'node',
verbose: true,
automock: false,
globalSetup: '<rootDir>/_config/jest.global.setup.ts',
globalTeardown: '<rootDir>/_config/jest.global.teardown.ts',
modulePaths: [serverSrc], // <-- This will be set to 'baseUrl' value
moduleNameMapper: pathsToModuleNameMapper(aliases /* ,{ prefix: '<rootDir>/' } */),
};
export default config;
jest.global.setup.ts
import _path from 'path';
import * as TSConfigPaths from 'tsconfig-paths';
import { aliases } from './jest.config';
const baseUrl: string = _path.resolve(__dirname, '..');
// @ts-ignore: TS6133
const cleanup: () => void = TSConfigPaths.register({
baseUrl,
paths: aliases, // tsConfig.compilerOptions.paths,
});
import ExpressApp from '@/controllers/express-server';
import PublicController from '@/controllers/public';
export const globalSetup: () => Promise<void>
= async (): Promise<void> => {
const expressApp: ExpressApp | null = new ExpressApp();
expressApp?.initializeControllers([
new PublicController(expressApp),
]);
(globalThis as any).__ExpressApp__ = expressApp;
};
// afterAll(async (): Promise<void> => {
// });
export default globalSetup;
最后,public.test.ts
import request from 'supertest';
import express from 'express';
import { StatusCodes } from 'http-status-codes';
import ExpressApp from '@/controllers/express-server';
describe('public.test.ts', () => {
//let agent: request.Agent;
//let tmp: any = null;
beforeAll(async (): Promise<void> => {
ExpressApp.me = (globalThis as any).__ExpressApp__;
//agent = request.agent(ExpressApp.me?.app || undefined);
});
test('get /public/v1/version', async (): Promise<void> => {
const expressApp: ExpressApp | null = ExpressApp.me;
const response: any = await request(expressApp?.app as express.Application)
.get('/public/v1/version');
expect(response.statusCode).toBe(StatusCodes.OK);
expect(response.body).toBe('1.0.0.0');
});
});