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】:

UserContextapp/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.spyOn “方法名称”参数

Jest spyOn 函数调用

[Jest] Restore the Original Implementation of a Mocked JavaScript Function with jest.spyOn

如何使用 jest.spyOn 测试 RTKQuery 端点

useEffect 钩子没有被 jest.spyOn 嘲笑