我在我的项目中使用ESM。我所有的
.ts
文件都被转换为存储在“dist”文件夹中的 .js
。我的目录结构的示例布局是:
dist/
├─ src/
│ ├─ store/
│ │ ├─ index.js
│ ├─ strategies/
│ │ ├─ scalping/
│ │ │ ├─ main.js
src/
├─ store/
│ │ ├─ index.ts
├─ strategies/
│ │ ├─ scalping/
│ │ │ ├─ main.ts
test/
├─ strategies/
│ │ ├─ scalping/
│ │ │ ├─ main.test.ts
我的 tsconfig.json 中的
moduleResolution
是“NodeNext”
我的
jest.config.ts
是:
import type { Config } from "jest"
const config: Config = {
preset: 'ts-jest/presets/default-esm',
displayName: "scalper",
testTimeout: 60000, // 30 secs - Necessary for when testing timers
globalSetup: "<rootDir>/test/jest/setup.ts",
globalTeardown: "<rootDir>/test/jest/teardown.ts",
coveragePathIgnorePatterns: [ "/node_modules/" ],
testMatch: ["<rootDir>/test/**/?(*.)+(spec|test).[jt]s?(x)"],
modulePathIgnorePatterns: ["<rootDir>/test/.*/__mocks__", "<rootDir>/dist/"],
testEnvironment: "node",
coverageDirectory: "<rootDir>/coverage",
extensionsToTreatAsEsm: [".ts"],
moduleNameMapper: {
"^(.*)\\.js$": "$1",
},
transform: {
'^.+\\.ts?$': [
'ts-jest',
{
useESM: true
}
]
},
}
export default config
我的
test/main/index.test.ts
是:
import { jest } from '@jest/globals'
import * as fs from "fs"
import * as path from "path"
import appRoot from "app-root-path"
const params = JSON.parse(fs.readFileSync(path.join(appRoot.path, 'test/strategies/scalping/main/params.json'), 'utf8'))
import * as strategies from "../../../../src/strategies/scalping/index.js"
describe('main', () => {
it('Should create a valid scalp trade', async () => {
const { builtMarket, config } = params
// Mocking of ESM requires dynamic imports
// https://jestjs.io/docs/ecmascript-modules#module-mocking-in-esm
jest.unstable_mockModule('../../../../src/store/index.js', () => ({
getScalpRunners: jest.fn().mockResolvedValue(params as never)
}))
const store = await import('../../../../src/store/index.js')
await strategies.scalping(builtMarket, config)
})
})
方法
getScalpRunners
我本以为会被嘲笑,但它不是&调用主要定义。我查看了以下模拟 ESM 模块的文档(在代码中链接)并且我查看了这个问题,但它仍然不起作用。
我的
src/strategies/scalping/main.ts
的一个片段是:
import store from "../../store/index.js"
export default async function(builtMarket: BuiltMarket, config: Config): Promise<BuiltMarket | undefined> {
try {
const validMarket = isValidMarket(builtMarket, config)
if (validMarket) {
const polledRunners: ScalpRunner[] | undefined = await store.getScalpRunners({
eventId: builtMarket._eventId,
marketId: builtMarket._marketId
})
// other stuff...
}
} catch(err: any) {
throw err
}
}
为什么这没有被正确地嘲笑?
编辑:
如果我在
store
中输出 index.test.ts
的值,那么它就是模块的模拟版本。然而,在 main.ts
中,它是模块的未模拟版本。
我解决了这个问题,但看起来很混乱。我将
../../../../src/strategies/scalping/index.js
的导入更改为动态导入而不是静态导入,以确保它使用的是模拟导入,并且必须模拟 store/index.js
的默认导出。
index.test.ts
:
// Not usually required to import global but apparently the use of ESM does need it
// https://jestjs.io/docs/ecmascript-modules#differences-between-esm-and-commonjs
import { jest } from '@jest/globals'
// Mocking of ESM requires dynamic imports
// https://jestjs.io/docs/ecmascript-modules#module-mocking-in-esm
jest.unstable_mockModule('../../../../src/store/index.js', () => ({
default: {
getScalpRunners: jest.fn().mockResolvedValue(params as never)
}
}))
import * as fs from "fs"
import * as path from "path"
import appRoot from "app-root-path"
const params = JSON.parse(fs.readFileSync(path.join(appRoot.path, 'test/strategies/scalping/main/params.json'), 'utf8'))
// Mocking of ESM requires dynamic imports
// This ensures that the import is done AFTER the mock
const store = await import('../../../../src/store/index.js')
const strategies = await import('../../../../src/strategies/scalping/index.js')
describe('main', () => {
it('Should create a valid scalp trade', async () => {
const { builtMarket, config } = params
await strategies.scalping(builtMarket, config)
})
})