【问题标题】:Automatic mock of an ES6 class fails (Jest, Vanilla JavaScript)ES6 类的自动模拟失败(Jest,Vanilla JavaScript)
【发布时间】:2021-11-24 23:46:36
【问题描述】:

我有一个 FooStorage 类,它有一个 Foo 对象数组作为成员变量。在测试FooStorage 时,我想模拟类Foo
ES6 Class Mocks 所述,在我的情况下,我只需要一个自动模拟。但是当我试图模拟这个类时,它似乎并没有成功。相反,当我尝试使用 mockClear() 重置模拟时,我收到一条错误消息。

以下是 jest 的代码和输出:


foo.js

class Foo {};

export default Foo;

foostorage.js

import Foo from "./foo.js";

class FooStorage {
    constructor() {
        this.storage = []; // Array of Foo objects
    }
}

export default FooStorage;

foostorage.test.js

import Foo from "../src/foo.js";
import FooStorage from "../src/foostorage.js";

import { jest } from "@jest/globals";

jest.mock("../src/foo.js");

beforeEach(() => {
    Foo.mockClear();
});

test("if the Foo constructor hasn`t been called", () => {
    const storage = new FooStorage();
    expect(Foo).not.toHaveBeenCalled();
});

output

if the Foo constructor hasn`t been called

TypeError: Foo.mockClear is not a function

   7 |
   8 | beforeEach(() => {
>  9 |      Foo.mockClear();
     |          ^
  10 | });
  11 |
  12 | test("if the Foo constructor hasn`t been called", () => {

  at Object.<anonymous> (test/foostorage.test.js:9:6)

我已经尝试将jest.mock("../src/foo.js"); 放在import Foo from "../src/foo.js"; 之前,但问题没有解决。


编辑:

我正在使用 Jest v. 27.0.6jest-environment-node v. 27.0.6@types/jest v. 27.0.1
我还使用了 nodejs 参数 --experimental-modules--experimental-vm-modules,这样我就可以使用 ES6 导入。我不使用 Babel 或其他任何东西。只是普通的 JavaScript。

【问题讨论】:

  • 您使用的是哪个笑话版本?您要在哪里测试它是 Javascript 中的普通项目吗?
  • 我也使用 nodejs 参数 - 不要。确保您遵循此处jestjs.io/docs/ecmascript-modules 的任何建议。这将使 Jest 以常规模式运行,这是答案所假定的。 .缺乏对本机 ESM 的 Jest 支持,并且会导致模拟问题。这就是文档中明确说明的内容,请注意,我们目前在 ESM 中不以干净的方式支持 jest.mock
  • @EstusFlask 我改用Babel,现在问题解决了。如果需要,您可以将您的评论作为答案发布或编辑您之前的评论,以便我将其标记为已接受的答案。

标签: javascript unit-testing ecmascript-6 jestjs mocking


【解决方案1】:

自动模拟应该像文档解释的那样工作。如果它没有并导致所述错误,这意味着该模块没有被模拟,因此它也会因手动模拟而失败。这可能是因为未正确执行模拟(模块路径中的不同大小写或扩展名),或者 jest.mock 提升不起作用(最常见的原因是 Babel 转换配置错误)。

此代码与文档中显示的不同之处在于,jest global 是常用的,而这里是导入值,因此与使用jest global 不同。这很明显,known reason 提升的工作方式有所不同。

它可以可能通过以不干扰预期执行顺序的方式对依赖项进行排序来修复:

import { jest } from "@jest/globals";

jest.mock("../src/foo.js");

import Foo from "../src/foo.js";
...

jest.mock 的提升依赖于未记录的、不符合规范的 hack,这也取决于 Jest 版本和特定设置。它不能保证有效,因为 ES 导入预计会按规范提升到其他语句之上,并且没有排除。

一个过时但稳定的解决方法是切换到require,这是常规的 JavaScript 函数调用并按原样进行评估:

let { jest } = require ("@jest/globals");

jest.mock("../src/foo.js");

let { default: Foo } = require("../src/foo.js");
...

一个有效的解决方案是摆脱 jest 导入并设置具有相应全局变量的通用 Jest 环境,因此无论 Jest 版本和其他情况如何,jest.mock 都可以按预期提升。

【讨论】:

  • 更改导入和模拟的顺序,就像您在第一个代码块中指出的那样,不幸的是对我不起作用。此外,当我删除 import { jest } from "@jest/globals"; 时,我会收到错误消息 ReferenceError: jest is not defined。当我切换到let { jest } = require ("@jest/globals"); 时,我得到ReferenceError: require is not defined
  • 这是一个特定于您的设置的问题。您不会在推荐的 Jest 设置中使用它。 Jest 全局变量(jestbeforeEach 等)默认公开且不需要导入,require 在执行 Jest 的 Node 环境中自然可用,除非您有可能影响它的不需要的 Babel 转换。
  • 我在帖子中添加了有关我的设置的信息。还有一些开玩笑的全局变量(例如describetestbeforeAll)在没有import { jest } from "@jest/globals"; 的情况下工作。没有它,只有jest 似乎无法工作。此外,我在package.json 中使用了"type": "module",这样我就可以使用ES6 导入(这是一个前端项目)。
  • 我在另一条评论中解释了问题所在。问题是您尝试将 Jet 与本机 ESM 一起使用。不需要使用import。多年来,Jest 一直通过 Babel 转换在推荐配置中支持 ESM。 "type": "module" 主要用于 Node.js。对前端项目没有任何好处,因为浏览器不直接支持 NPM 包和 package.json。
猜你喜欢
  • 2018-05-04
  • 2018-03-12
  • 1970-01-01
  • 2017-09-30
  • 1970-01-01
  • 2020-02-02
  • 2020-11-05
  • 2018-09-07
  • 2019-07-24
相关资源
最近更新 更多