笑话间谍On不适用于索引文件,无法重新定义属性

Cod*_*rit 78 typescript reactjs jestjs spyon

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

如果我监视src/app/context/user-context它,但将导入更改为src/app/context我得到:

TypeError: Cannot redefine property: useUser at Function.defineProperty (<anonymous>)
Run Code Online (Sandbox Code Playgroud)

这是为什么?

源代码:

TypeError: Cannot redefine property: useUser at Function.defineProperty (<anonymous>)
Run Code Online (Sandbox Code Playgroud)
// src/app/context/user-context.tsx

export const UserContext = React.createContext({});

export function useUser() {
  return useContext(UserContext);;
}

Run Code Online (Sandbox Code Playgroud)
// src/app/context/index.tsx

export * from "./user-context";
Run Code Online (Sandbox Code Playgroud)

Spe*_*ord 96

不同意使用jest.mock()代替spyOn()是一个好的解决方案。使用这种方法,您必须在规范文件的顶部模拟所有函数。

但某些测试用例可能需要不同的设置或保持真实。

无论如何,我想继续使用spyOn()

更好的方法是在规范文件的顶部添加以下行(就在导入之后和之前describe())。

import * as Foo from 'path/to/file'; 

jest.mock('path/to/file', () => {
  return {
    __esModule: true,    //    <----- this __esModule: true is important
    ...jest.requireActual('path/to/file')
  };
});

...

//just do a normal spyOn() as you did before somewhere in your test:
jest.spyOn(Foo, 'fn');
Run Code Online (Sandbox Code Playgroud)

PS 也可以是一句单行话:

jest.mock('path/to/file', () => ({ __esModule: true, ...jest.requireActual('path/to/file') }));
Run Code Online (Sandbox Code Playgroud)

PPS 设置'path/to/file'为常量可能不起作用。至少对我来说没有。遗憾的是,您需要重复实际路径 3 次。

  • @PouyaAtaei你不是在监视模拟,因为你正在做“jest.requireActual”。我对这个解决方案也不满意,但找不到更好的解决方案。也许更好的解决方案是调整 babel 编译单元测试文件的方式(使用“configurable: true”),但我想为此你需要创建一个 babel 插件 (4认同)

die*_*edu 41

如果你看一下为重新导出生成的js 代码,它看起来像这样

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];
    }
  });
});
Run Code Online (Sandbox Code Playgroud)

您得到的错误是由于编译器没有添加 的configurable: true选项defineProperty,这将允许笑话重新定义导出以模拟它,来自文档

可配置

如果可以更改此属性描述符的类型并且可以从相应的对象中删除该属性,则为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
      })
    }
  })
});

Run Code Online (Sandbox Code Playgroud)

  • 如果缺乏“可配置”的情况,babel 的配置假设“constantReexports”不会解决这个问题吗?[检查此处](https://babeljs.io/repl#?browsers=ie%2010&amp;build=&amp;builtIns=usage&amp;corejs=3.6&amp;spec=false&amp;loose=false&amp;code_lz=KYDwDg9gTgLgBAKjgMyhAtnARAOgPQCuAzsFALQDGEAdjKDFgNxA&amp;debug=false&amp;forceAllTransforms=false&amp;shippedProposals=false&amp;circle ciRepo=&amp;evaluate=false&amp;fileSize=false&amp;timeTravel=false&amp;sourceType=module&amp;lineWrap = false&amp;presets=env%2Creact&amp;prettier=false&amp;targets=&amp;version=7.16.3&amp;externalPlugins=&amp;asstitutions=%7B%22constantReexports%22%3Atrue%7D)。但对我来说不起作用 (2认同)