jest spyOn 无法处理索引文件,无法重新定义属性
Posted
技术标签:
【中文标题】jest spyOn 无法处理索引文件,无法重新定义属性【英文标题】:jest spyOn not working on index file, cannot redefine property 【发布时间】:2021-08-24 14:19:40 【问题描述】:我有UserContext
和一个从src/app/context/user-context.tsx
导出的钩子useUser
。
此外,我在src/app/context
中有一个 index.tsx 文件,它导出所有子模块。
如果我监视 src/app/context/user-context
它可以工作,但将导入更改为 src/app/context
我得到:
TypeError: Cannot redefine property: useUser at Function.defineProperty (<anonymous>)
为什么会这样?
源代码:
// src/app/context/user-context.tsx
export const UserContext = React.createContext();
export function useUser()
return useContext(UserContext);;
// src/app/context/index.tsx
export * from "./user-context";
// *.spec.tsx
// This works:
import * as UserContext from "src/app/context/user-context";
// This does not work:
// import * as UserContext from "src/app/context";
it("should render complete navigation when user is logged in", () =>
jest.spyOn(UserContext, "useUser").mockReturnValue(
user: mockUser,
update: (user) => null,
initialized: true,
);
)
【问题讨论】:
你是如何在被测试的代码中导入UserContext
的?如果您从“src/app/context/user-context”导入,那么您需要在规范文件中以相同的方式导入它,否则它不会模拟相同的东西。虽然您的 TypeError 似乎是一个不同的问题......
【参考方案1】:
UserContext
从 app/context/index.tsx
重新导出时会引发该问题,因为它是 Typescript 在 3.9 之前的版本中如何处理重新导出的错误。
这个问题在 3.9 版本已经修复,所以将你项目中的 Typescript 升级到这个版本或更高版本。
这个问题是reported here 并已解决 与 fix here 上的 cmets
以下是不升级版本的解决方法。
在您的index.tsx
文件中有一个对象,其属性作为导入的方法,然后导出该对象。
在src/app/context/index.tsx
内
import useUser from './context/user-context.tsx'
const context =
useUser,
otherFunctionsIfAny
export default context;
或者这也应该有效,
import * as useUser from './context/user-context.tsx';
export useUser ;
export default useUser;
然后监视他们,
import * as UserContext from "src/app/context";
it("should render complete navigation when user is logged in", () =>
jest.spyOn(UserContext, "useUser").mockReturnValue(
user: mockUser,
update: (user) => null,
initialized: true,
);
);
Ref
提示:- 除了重新导出的问题外,以前的版本也不支持实时绑定,即,当导出模块发生变化时,导入模块无法看到发生的变化在出口商方面。
例如:
Test.js
let num = 10;
function add()
++num; // Value is mutated
exports.num = num;
exports.add = add;
index.js
一个similar issue,但由于导入的路径。
此错误消息(javascript)的原因在这篇文章TypeError: Cannot redefine property: Function.defineProperty ()中进行了解释
【讨论】:
我们使用的是 TS 4.4.4,但还是有问题;我相信这是esbuild
的问题?【参考方案2】:
如果您查看 js 代码 produced 进行重新导出,它看起来像这样
Object.defineProperty(exports, "__esModule",
value: true
);
var _context = require("context");
Object.keys(_context).forEach(function (key)
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _context[key]) return;
Object.defineProperty(exports, key,
enumerable: true,
get: function get()
return _context[key];
);
);
您得到的错误是由于编译器没有在defineProperty
的选项中添加configurable: true
,这将允许开玩笑重新定义导出以模拟它,来自docs
可配置
true 如果此属性描述符的类型可以更改并且如果 可以从相应的对象中删除属性。 默认为
false
。
我认为您可以以某种方式调整您的配置以使编译器添加它,但这完全取决于您使用的工具。
一种更易于访问的方法是使用 jest.mock
而不是 jest.spyOn
来模拟用户上下文文件,而不是尝试重新定义不可配置的导出
it("should render complete navigation when user is logged in", () =>
jest.mock('./user-context', () =>
return
...jest.requireActual('./user-context'),
useUser: jest.fn().mockReturnValue(
user: ,
update: (user: any) => null,
initialized: true
)
)
);
【讨论】:
以上是关于jest spyOn 无法处理索引文件,无法重新定义属性的主要内容,如果未能解决你的问题,请参考以下文章
Jest spyOn 期间的 TypeError:无法设置只有一个 getter 的 #<Object> 的属性 getRequest
[Jest] Restore the Original Implementation of a Mocked JavaScript Function with jest.spyOn