开玩笑 ReferenceError:初始化前无法访问 ''

Suf*_*ane 76 javascript unit-testing node.js typescript jestjs

我收到错误:

ReferenceError: Cannot access 'myMock' before initialization

尽管我尊重有关提升的笑话文档: A limitation with the factory parameter is that, since calls to jest.mock() are hoisted to the top of the file, it's not possible to first define a variable and then use it in the factory. An exception is made for variables that start with the word 'mock'.

我正在这样做:

import MyClass from './my_class';
import * as anotherClass from './another_class';

const mockMethod1 = jest.fn();
const mockMethod2 = jest.fn();
jest.mock('./my_class', () => {
  return {
    default: {
      staticMethod: jest.fn().mockReturnValue(
        {
          method1: mockMethod1,
          method2: mockMethod2,
        })
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,我的两个变量都遵循“标准”,但没有正确提升。

我错过了什么吗?

显然,当我只是传递jest.fn()而不是我的变量时,它是有效的,但我不确定如何在稍后的测试中使用它们。

Mau*_*ius 68

上面的答案都没有解决我的问题,所以这是我的解决方案:

const mockMyMethod = jest.fn();

jest.mock('some-package', () => ({
  myMethod: () => mockMyMethod()
}));
Run Code Online (Sandbox Code Playgroud)

编辑

之前的解决方案使用提升var,但这是一个奇怪的解决方法。更新后的解决方案无需以 jest 满意的方式进行提升。它之所以有效,是因为在从模拟包调用mockMyMethod该方法之前,该函数并未实际执行。myMethod

  • 这是因为:1) [Jest Hoists](https://jestjs.io/docs/manual-mocks#using-with-es-module-imports) `jest.mock()` 调用。2) [Jest 不会提升](https://github.com/kulshekhar/ts-jest/issues/1088#issuecomment-562975615) 以 `mock` 开头的变量。3) 在 JavaScript 中用 `var` 声明的变量[总是被提升](/sf/answers/801109151/),而用 `let` 和 `const` 声明的变量则不会。 (13认同)
  • 我尝试在“jest.mock”之外使用“let”在内部分配它,但失败了。使用“var”解决了我的问题。谢谢。 (6认同)

Jas*_*son 30

当您需要监视声明时,接受的答案不会处理const,因为它是在模块工厂范围内定义的。

对我来说,模块工厂需要位于最终导入您想要模拟的内容的任何导入语句之上。这是使用带有prisma库的Nestjs的代码片段。

// app.e2e.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import mockPrismaClient from './utils/mockPrismaClient'; // you can assert, spy, etc. on this object in your test suites.

// must define this above the `AppModule` import, otherwise the ReferenceError is raised.
jest.mock('@prisma/client', () => {
  return {
    PrismaClient: jest.fn().mockImplementation(() => mockPrismaClient),
  };
});

import { AppModule } from './../src/app.module'; // somwhere here, the prisma is imported

describe('AppController (e2e)', () => {
  let app: INestApplication;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });
)};
Run Code Online (Sandbox Code Playgroud)

  • 事实上,这并不是解决问题的正确方法。将任何内容放在导入之上并期望按此顺序进行评估在技术上是错误的,因为 ESM 导入是由规范提升的,而 jest.mock 是由 Jest 通过 Babel 转换提升的,这也是指定的。它可能在一种设置中工作,但在另一种设置中失败,因为行为尚未确定。在模拟中使用mockPrismaClient的正确方法是在“jest.mock”中使用“require”或“jest.requireActual”导入它,而不是依赖父作用域中的值。 (9认同)

Mat*_*att 24

为了澄清Jason Limantoro所说的内容,请将const上面的内容移至导入模块的位置:

const mockMethod1 = jest.fn(); // Defined here before import.
const mockMethod2 = jest.fn();

import MyClass from './my_class'; // Imported here.
import * as anotherClass from './another_class';

jest.mock('./my_class', () => {
  return {
    default: {
      staticMethod: jest.fn().mockReturnValue(
        {
          method1: mockMethod1,
          method2: mockMethod2,
        })
    }
  }
});
Run Code Online (Sandbox Code Playgroud)


Ric*_*lde 15

你应该把你的嘲笑移到你的导入之上;这可能是你的问题的根源。导入也会被提升,因此多个提升的条目将按顺序提升。

jest.mock('./my_class', () => {
  const mockMethod = jest.fn() 
  const default = { staticMethod: jest.fn().mockReturnValue({ method: mockMethod }) };
  return { default, mockMethod };
});

import MyClass, { mockMethod } from './my_class';  // will import your mock
import * as anotherClass from './another_class';
Run Code Online (Sandbox Code Playgroud)

但是,如果由于某种原因您无法做到这一点,您可以使用doMock以避免提升行为。如果这种情况发生在文件的顶部,则应该是 1 比 1 的更改。

var mockMyMethod = jest.fn();
jest.doMock('some-package', () => ({ myMethod: mockMyMethod }));
Run Code Online (Sandbox Code Playgroud)

我还建议使用var代替constlet用于模拟。他们的提升行为略有不同,我发现有几次使用var似乎与 jest 自己的提升效果更好。

  • doMock 就是答案 - 谢谢 (4认同)

Est*_*ask 11

文档解决的问题是已jest.mock提升但const声明未提升。这会导致工厂函数在导入模拟模块时被评估,并且变量处于临时死区。

如果需要访问嵌套的模拟函数,则需要将它们作为导出对象的一部分公开:

jest.mock('./my_class', () => {
  const mockMethod1 = jest.fn();
  const mockMethod2 = jest.fn();
  return {
    __esModule: true,
    mockMethod1,
    mockMethod2,
    default: {
      ...
Run Code Online (Sandbox Code Playgroud)

这也适用于手动模拟,其中__mocks__变量只能在模拟内部访问。

  • 您先生是救世主!它就像一个魅力! (3认同)
  • @Sam 他们可以更好地解释他们警告的案例。明显的区别是文档中没有对导入产生副作用的“anotherClass”。 (2认同)

Vin*_*sCR 5

我遇到了同样的问题。

ReferenceError:初始化前无法访问“mockYourFunction”

所选答案有效,但解释错误,不是因为 import order。它起作用的原因是因为答案更改mockResturnValuemockImplementation.

mockResturnValue将尝试立即解析该值,而 mockImplementation仅在调用时才执行此操作,前提是变量在需要时需要时间。

一些,为了举例说明一个可行的解决方案,无需重新排序导入或添加 var 并避免在 PR 中出现一些激烈的评论,以下是代码的重要部分:

const mockPrismaClient = jest.fn();
jest.mock('@prisma/client', () => {
  return {
    PrismaClient: jest.fn().mockImplementation(() => mockPrismaClient),
  };
});
Run Code Online (Sandbox Code Playgroud)