在Jest中重置节点模块

时间:2019-07-07 10:25:18

标签: javascript node.js unit-testing jestjs

我有一个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");中导入应用程序也是如此。

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

3 个答案:

答案 0 :(得分: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");
      ...

我希望这会有所帮助!不仅在玩笑中,嘲笑还很棘手。

参考

答案 1 :(得分:0)

您在测试范围内模拟os模块,但是实际代码在其自己的范围内使用该模块。 jest.mock仅采用导出的方法并将其替换为jest.fn,因此,从理论上讲,您的代码需要导出os,然后您的测试代码仅需要代码一次< / em>在文件顶部。您的测试代码不应直接导入os

顺便说一下,从阅读a tutorial on Jest mocks来看,这只是未经检验的理论。

答案 2 :(得分:0)

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

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

    //...
})