jest spyOn 无法处理索引文件,无法重新定义 属性

jest spyOn not working on index file, cannot redefine property

我有 UserContext 和一个从 src/app/context/user-context.tsx 导出的钩子 useUser。 此外,我在 src/app/context 中有一个 index.tsx 文件,它导出所有子模块。

如果我 spyOn 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,
    });
})

app/context/index.tsx 重新导出时 UserContext 会抛出该问题,因为它是 Typescript 在 3.9 之前的版本中如何处理重新导出的错误。

此问题已从 3.9 版本开始修复,因此请将项目中的 Typescript 升级到此版本或更高版本。

This issue was reported here and resolved with comments on the fix here

以下是无需版本升级的解决方法。

在您的 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

A similar issue 但由于导入的路径。

此错误消息 (JavaScript) 的原因在此 post TypeError: Cannot redefine property: Function.defineProperty ()

中进行了解释

如果您查看重新导出的 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,这将允许 jest 重新定义导出以模拟它,来自 docs

configurable

true if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object. Defaults to 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
      })
    }
  })
});