我有一个带有两个函数的TypeScript模块:foo()调用bar()。我想模拟bar()并从单元测试中调用foo()。我试过了:
foo.ts:
export function bar(){
throw 'not implemented'
}
export function foo(){
return bar();
}
foo.test.ts:
import * as foo from './foo'
test('TODO', () => {
jest.spyOn(foo, 'bar').mockReturnValue('quux')
expect(foo.foo()).toBe('quux')
});
但是我得到了一个错误:
FAIL ./foo.test.ts
✕ TODO (4 ms)
● TODO
thrown: "not implemented"
1 | import * as foo from './foo'
2 |
> 3 | test('TODO', () => {
| ^
4 | jest.spyOn(foo, 'bar').mockReturnValue('quux')
5 | expect(foo.foo()).toBe('quux')
6 | });
at Object.<anonymous> (foo.test.ts:3:1)
at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:333:13)
似乎foo()以某种方式绕过了模拟。我如何使它使用模拟而不是真正的bar()?
修改foo()调用exports.bar()而不是bar():
export function foo(){
return exports.bar();
}
示例中的foo()绕过了mock,因为有两个名称空间:模块和exports对象。当对bar()的引用被解析后,JavaScript和TypeScript会在模块命名空间中搜索它,但Jest只能监视exports对象,不能改变模块的内容。
一种可能的解决方案是通过如上所示的exports对象显式查找要模拟的标识符。
我在这里找到了这个方法:https://medium.com/welldone-software/jest-how-to-mock-a-function-call-inside-a-module-21c05c57a39f#576f
try this refactor:
foo.ts:
const fn = {
bar(): unknown | void {
throw "not implemented";
},
foo() {
return fn.bar();
},
};
export default fn
foo.test.ts:
import functionsUnderTest from "./foo";
beforeEach(() => {
jest.spyOn(functionsUnderTest, "bar").mockReturnValue("quux");
});
test("TODO", () => {
expect(functionsUnderTest.foo()).toBe("quux");
});
名称空间的问题通过这种方式解决