【问题标题】:Reseting node module in Jest在 Jest 中重置节点模块
【发布时间】:2021-04-02 15:27:48
【问题描述】:

我有一个 Node.js 应用程序,其中index.js 对类 Unix 和 Windows 平台有不同的导出。

import os from "os";

function throwNotSupportedError() {
  throw new Error("Platform not supported.");
}

console.log(os.platform());

switch (os.platform()) {
  case "darwin":
  case "linux":
    module.exports = {
      foo: require("./unix/foo"),
      bar: require("./unix/bar")
    };
    break;
  case "win32":
    module.exports = {
      foo: require("./win32/foo"),
      bar: require("./win32/bar")
    };
    break;
  default:
    throwNotSupportedError();
}

我正在尝试使用如下所示的单元测试来覆盖此文件:

import os from "os";

jest.mock("os");

describe("Linux platform", () => {
  test("has `foo` and `bar` methods on Linux platform", () => {
    os.platform.mockImplementation(() => "linux");

    const app = require("../src");
    expect(app.foo).toBeTruthy();
    expect(app.bar).toBeTruthy();
  });
});

describe("Windows platform", () => {
  test("has `foo` and `bar` methods on Windows platform", () => {
    os.platform.mockImplementation(() => "win32");

    const app = require("../src");
    expect(app.foo).toBeTruthy();
    expect(app.bar).toBeTruthy();
  });
});

问题是os.platform.mockImplementation(() => "win32"); 有效,但console.log(os.platform()); 仍然显示linux,即使我在每个测试用例const app = require("../src"); 中导入应用程序。

我的错误在哪里以及如何解决?

【问题讨论】:

    标签: javascript node.js unit-testing jestjs


    【解决方案1】:

    Khang 关于jest.resetModules() 的回答指向了正确的方向。我想补充一点,当您重置模块时,对任何过去导入的引用都将被“忽略”(重置后会创建一个新实例)。也就是说,在模块重置后,测试顶部的import os from "os"; 将不再使用。

    解决方案

    除了jest.resetModules(),您还需要在您即将执行的测试中重新导入(或在这种情况下重新要求)os 模块。通过这样做,os.platform.mockImplementation(() => "win32"); 将应用于模块模拟的最新实例。您的两个测试都需要以这种方式构建;

    test("has `foo` and `bar` methods on Windows platform", () => {
      const os = require('os');
      os.platform.mockImplementation(() => "win32");
    
      const app = require("./os-test");
      expect(app.foo).toBeTruthy();
      expect(app.bar).toBeTruthy();
    });
    

    您可能希望使用beforeEach 而不是afterEach,以确保os 模块在测试前是干净的。开玩笑应该隔离每个测试文件,但比抱歉更安全?最后,您希望在所有测试之前运行beforeEach,而不仅仅是在“Windows 平台”describe 内运行。为此,您可以将其移动到文件的根目录或将两个 describe 包装在一个额外的 describe 中,例如

    jest.mock("os");
    
    describe('Platform specific module', () => {
      beforeEach(() => {
        jest.resetModules();
      });
    
      describe("Linux platform", () => {
        test("has `foo` and `bar` methods on Linux platform", () => {
          const os = require('os');
          os.platform.mockImplementation(() => "linux");
          ...
    

    我希望这会有所帮助!模拟往往很棘手,不仅仅是在 Jest 中。

    参考文献

    【讨论】:

      【解决方案2】:

      您在测试范围内模拟 os 模块,但您的实际代码在其自己的范围内使用该模块。 jest.mock 只接受导出的方法并用jest.fn 替换它们,所以理论上你的代码需要导出os,那么你的测试代码应该只需要你的代码在顶部一次的文件。您的测试代码不应直接导入os

      顺便说一句,这只是未经检验的理论,来自阅读a tutorial on Jest mocks

      【讨论】:

        【解决方案3】:

        您需要在每次测试后使用jest.resetModules() 来清除模块缓存:

        describe("Windows platform", () => {
            afterEach(() => {
                jest.resetModules();
            })
        
            //...
        })
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-01-22
          • 2018-07-14
          • 2018-01-16
          • 2022-11-09
          • 2019-04-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多