跳至主要內容

TheJestObject

Mr.Dylin...大约 13 分钟单元测试H_单元测试10.单元测试文档

The Jest Object(Jest 对象)

jest 对象自动在每个测试文件的范围内存在。jest 对象中的方法有助于创建模拟并让控制 Jest 的整体行为。也可以通过 import {jest} from '@jest/globals' 显式导入。

methods - 方法


Reference - 参考

Mock Modules

jest.disableAutomock()

在模块加载器中禁用自动模拟。

更多信息请参阅 configurationautomock 部分

调用此方法后,所有 require() 将返回每个模块的真实版本(而不是模拟版本)。

Jest 配置:

{
  "automock": true
}

例子:

// utils.js
export default {
  authorize: () => {
    return "token";
  },
};
// __tests__/disableAutomocking.js
import utils from "../utils";

jest.disableAutomock();

test("original implementation", () => {
  // 现在我们有了最初的实现方法
  // 即便是我们在 Jest 中配置了自动模拟
  expect(utils.authorize()).toBe("token");
});

当依赖项要模拟的数量远小于不模拟的数量时,这通常很有用。例如,如果你正在编写测试模块使用了大量的依赖项,这些依赖项可以合理地归类为模块的“实现细节”,那么你可能不想模拟它们。

可能被视为“实现细节”的依赖项示例包括从语言内置方法(例如 Array.prototype)到高度通用的实用程序方法(例如下划线/lodash、数组实用程序等)和整个库(如 React.js)。

返回用于链接的 jest 对象。

注意:此方法以前称为 autoMockOff。使用 babel-jest 时,对 disableAutomock 的调用将自动提升到代码块的顶部。如果想明确避免这种行为请使用 autoMockOff

jest.enableAutomock()

在模块加载器中启用自动模拟。

返回用于链接的 jest 对象。

更多信息请参阅 configurationautomock 部分

例子:

// utils.js
export default {
  authorize: () => {
    return "token";
  },
  isAuthorized: (secret) => secret === "wizard",
};
// __tests__/enableAutomocking.js
jest.enableAutomock();

import utils from "../utils";

test("original implementation", () => {
  // 现在我们有了模拟的实现
  expect(utils.authorize._isMockFunction).toBeTruthy();
  expect(utils.isAuthorized._isMockFunction).toBeTruthy();
});

注意:此方法以前称为 autoMockOn。使用 babel-jest 时,对 enableAutomock 的调用将自动提升到代码块的顶部。如果想明确避免这种行为请使用 autoMockOn

jest.createMockFromModule(moduleName)

在 Jest 26.0.0+ 中重命名

别名:.genMockFromModule(moduleName)

给定模块的名称,使用自动模拟系统为你生成模块的模拟版本。

当你想要创建扩展自动模拟行为的手动模拟open in new window时,这很有用。

举个例子:

// utils.js
export default {
  authorize: () => {
    return "token";
  },
  isAuthorized: (secret) => secret === "wizard",
};
// __tests__/createMockFromModule.test.js
const utils = jest.createMockFromModule("../utils").default;
utils.isAuthorized = jest.fn((secret) => secret === "not wizard");

test("implementation created by jest.createMockFromModule", () => {
  expect(utils.authorize.mock).toBeTruthy();
  expect(utils.isAuthorized("not wizard")).toEqual(true);
});

这是 createMockFromModule 模拟以下数据类型的方式:

Function

创建一个新的 模拟函数open in new window,新函数没有形参,调用时将返回 undefined。此功能也适用于异步函数。

Class

创建一个新的类。原始类的接口被保留,所有的类成员函数和属性都将被模拟。

Object

创建一个新的深拷贝对象。对象的键被维护并且模拟它们的值。

Array

创建一个空数组,无视原有的数组。

Primitives

创建一个与原始属性具有相同值的新属性。

举个例子:

// example.js
module.exports = {
  function: function square(a, b) {
    return a * b;
  },
  asyncFunction: async function asyncSquare(a, b) {
    const result = (await a) * b;
    return result;
  },
  class: new (class Bar {
    constructor() {
      this.array = [1, 2, 3];
    }
    foo() {}
  })(),
  object: {
    baz: "foo",
    bar: {
      fiz: 1,
      buzz: [1, 2, 3],
    },
  },
  array: [1, 2, 3],
  number: 123,
  string: "baz",
  boolean: true,
  symbol: Symbol.for("a.b.c"),
};
// __tests__/example.test.js
const example = jest.createMockFromModule("./example");

test("should run example code", () => {
  // 创建一个新的模拟数组,没有正式的参数
  expect(example.function.name).toEqual("square");
  expect(example.function.length).toEqual(0);

  // 异步函数得到与标准同步函数相同的处理
  expect(example.asyncFunction.name).toEqual("asyncSquare");
  expect(example.asyncFunction.length).toEqual(0);

  // 创建一个具有相同接口的新类,成员函数和属性将被模拟。
  expect(example.class.constructor.name).toEqual("Bar");
  expect(example.class.foo.name).toEqual("foo");
  expect(example.class.array.length).toEqual(0);

  // 创建原始对象的深度克隆版本。
  expect(example.object).toEqual({
    baz: "foo",
    bar: {
      fiz: 1,
      buzz: [],
    },
  });

  // 创建一个空数组,无视原有的数组。
  expect(example.array.length).toEqual(0);

  // 创建一个与原始属性具有相同原始值的新属性。
  expect(example.number).toEqual(123);
  expect(example.string).toEqual("baz");
  expect(example.boolean).toEqual(true);
  expect(example.symbol).toEqual(Symbol.for("a.b.c"));
});

jest.mock(moduleName, factory?, options?)

在引用时模拟模块自动模拟版本。factoryoptions 是可选的。例如:

// banana.js
module.exports = () => "banana";

// __tests__/test.js
jest.mock("../banana");

const banana = require("../banana"); // banana 将被明确的模拟

banana(); // 将返回 'undefined' ,因为这个函数是自动模拟的

第二个参数是指定正在运行的方法,而不是使用 Jest 的自动模拟功能:

jest.mock("../moduleName", () => {
  return jest.fn(() => 42);
});

// 将调用 `jest.mock` 中的第二个方法参数
const moduleName = require("../moduleName");
moduleName(); // return '42';

当对默认导出的 ES6 模块使用 factory 参数时,需要指定 __esModule: true 属性。该属性一般由 Babel/TypeScript 生成,但这里需要手动设置。导入默认导出时,这是从导出对象导入名为 default 的属性的指令:

import moduleName, { foo } from "../moduleName";

jest.mock("../moduleName", () => {
  return {
    __esModule: true,
    default: jest.fn(() => 42),
    foo: jest.fn(() => 43),
  };
});

moduleName(); // return 42
foo(); // return 43

第三个参数可用于创建虚拟模拟——系统中不存在的模块的模拟:

jest.mock(
  "../moduleName",
  () => {
    /*
     * JS中不存在的模块的自定义实现,
     * 像 react-native 中生成的模块或原生模块。
     */
  },
  { virtual: true }
);

警告:在安装文件中导入模块(由 setupTestFrameworkScriptFile 指定)将防止对有问题的模块及其导入的所有模块进行模拟。

使用 jest.mock 模拟的模块仅针对调用 jest.mock 的文件进行模拟。另一个导入模块的文件将获得原始实现,即使它在模拟模块的测试文件之后运行。

返回用于链接的 jest 对象。

jest.unmock(moduleName)

表示模块系统不应从 require() 返回指定模块的模拟版本(例如,它应始终返回真实模块)。

此 API 最常见的用途是指定测试打算测试的模块(因此不想自动模拟)。

返回用于链接的 jest 对象。

jest.doMock(moduleName, factory, options)

使用 babel-jest 时,对 mock 的调用会自动提升到代码块的顶部。如果想明确避免这种行为,请使用此方法。

一个例子,当你想在同一个文件中以不同的方式模拟同一个模块时:

beforeEach(() => {
  jest.resetModules();
});

test("moduleName 1", () => {
  jest.doMock("../moduleName", () => {
    return jest.fn(() => 1);
  });
  const moduleName = require("../moduleName");
  expect(moduleName()).toEqual(1);
});

test("moduleName 2", () => {
  jest.doMock("../moduleName", () => {
    return jest.fn(() => 2);
  });
  const moduleName = require("../moduleName");
  expect(moduleName()).toEqual(2);
});

在 ES6 导入中使用 jest.doMock() 需要额外的步骤。如果不想在测试中使用 require,请遵循以下步骤:

  • 必须指定 __esModule: true 属性(有关更多信息,请参阅 jest.mock() API)。
  • 静态 ES6 模块导入被提升到文件的顶部,因此必须使用 import() 动态导入它们。
  • 最后,我们需要一个支持动态导入的环境。请参阅使用 Babelopen in new window 进行初始设置。然后将插件 babel-plugin-dynamic-import-nodeopen in new window 或跟它功能相同的插件添加到你的 Babel 配置中,以在 Node.js 中启用动态导入。
beforeEach(() => {
  jest.resetModules();
});

test("moduleName 1", () => {
  jest.doMock("../moduleName", () => {
    return {
      __esModule: true,
      default: "default1",
      foo: "foo1",
    };
  });
  return import("../moduleName").then((moduleName) => {
    expect(moduleName.default).toEqual("default1");
    expect(moduleName.foo).toEqual("foo1");
  });
});

test("moduleName 2", () => {
  jest.doMock("../moduleName", () => {
    return {
      __esModule: true,
      default: "default2",
      foo: "foo2",
    };
  });
  return import("../moduleName").then((moduleName) => {
    expect(moduleName.default).toEqual("default2");
    expect(moduleName.foo).toEqual("foo2");
  });
});

返回用于链接的 jest 对象。

jest.dontMock(moduleName)

使用 babel-jest 时,对 unmock 的调用将自动提升到代码块的顶部。如果想明确避免这种行为,请使用此方法。

返回用于链接的 jest 对象。

jest.setMock(moduleName, moduleExports)

显式提供模块系统应为指定模块返回的模拟对象。

模块系统通常提供的自动生成的模拟有时不足以满足你的测试需求。这些情况下,你应该编写一个更适合相关内容的手动模拟open in new window。在极少数情况下,即使是手动模拟也达不成你的目的,那么你需要在测试中自己构建模拟。

在这些罕见的情况下,你可以使用此 API 手动填充模块系统的模拟模块注册表中的插槽。

注意:建议使用 jest.mock() 代替。jest.mock API 的第二个参数是指定正在运行的方法,而不是使用 Jest 的自动模拟功能。

jest.requireActual(moduleName)

返回实际模块而不是模拟模块,绕过对模块是否应该接收模拟实现的所有检查。

jest.mock("../myModule", () => {
  // 导入的原始模块不被模拟
  const originalModule = jest.requireActual("../myModule");

  return {
    __esModule: true, // 在处理 esModules 时使用它
    ...originalModule,
    getRandom: jest.fn().mockReturnValue(10),
  };
});

const getRandom = require("../myModule").getRandom;

getRandom(); // 总是 return 10

jest.requireMock(moduleName)

返回一个模拟模块而不是实际模块,绕过对模块是否应该正常需要的所有检查。

jest.resetModules()

重置模块注册表 - 所有必需模块的缓存。这对于隔离在本地测试中模块的冲突状态很有用。

const sum1 = require("../sum");
jest.resetModules();
const sum2 = require("../sum");
sum1 === sum2;
// > false (两个 sum 模块都是 sum 模块的单独实例)

在测试中的例子:

beforeEach(() => {
  jest.resetModules();
});

test("works", () => {
  const sum = require("../sum");
});

test("works too", () => {
  const sum = require("../sum");
  // sum 是 sum 模块测试的副本不同。
});

返回用于链接的 jest 对象。

jest.isolateModules(fn)

jest.isolateModules(fn)jest.resetModules() 更进一步,并为回调函数中加载的模块创建一个沙箱注册表。这对于隔离每个测试的特定模块非常有用,这样本地模块的状态就不会在测试之间发生冲突。

let myModule;
jest.isolateModules(() => {
  myModule = require("myModule");
});

const otherCopyOfMyModule = require("myModule");

Mock functions

jest.fn(implementation?)

返回一个新的、未使用的模拟函数。可选的进行模拟 implementation

const mockFn = jest.fn();
mockFn();
expect(mockFn).toHaveBeenCalled();

// 自定义模拟实现
const returnsTrue = jest.fn(() => true);
console.log(returnsTrue()); // true;

jest.isMockFunction(fn)

确定给定的函数是否是模拟函数。

jest.spyOn(object, methodName)

创建一个类似于 jest.fn 的模拟函数,但跟踪对 object[methodName] 的调用。返回一个 Jest 模拟函数。

注意:默认情况下, jest.spyOn 也会调用 spied 方法。这是与其他大多数测试库不同的行为。如果要覆盖原来的函数,可以使用 jest.spyOn(object, methodName).mockImplementation(() => customImplementation)object[methodName] = jest.fn(() => customImplementation);

const video = {
  play() {
    return true;
  },
};

module.exports = video;

测试例子:

const video = require("./video");

test("plays video", () => {
  const spy = jest.spyOn(video, "play");
  const isPlaying = video.play();

  expect(spy).toHaveBeenCalled();
  expect(isPlaying).toBe(true);

  spy.mockRestore();
});

jest.spyOn(object, methodName, accessType?)

从 Jest 22.1.0+ 开始,jest.spyOn 方法接受一个可选的第三个 accessType 参数,它可以是 getset,这用于分别监视 getter 或 setter。

const video = {
  // getter!
  get play() {
    return true;
  },
};

module.exports = video;

const audio = {
  _volume: false,
  // setter!
  set volume(value) {
    this._volume = value;
  },
  get volume() {
    return this._volume;
  },
};

module.exports = audio;

测试例子:

const audio = require("./audio");
const video = require("./video");

test("plays video", () => {
  const spy = jest.spyOn(video, "play", "get"); // 调用 'get'
  const isPlaying = video.play;

  expect(spy).toHaveBeenCalled();
  expect(isPlaying).toBe(true);

  spy.mockRestore();
});

test("plays audio", () => {
  const spy = jest.spyOn(audio, "volume", "set"); // 调用 'set'
  audio.volume = 100;

  expect(spy).toHaveBeenCalled();
  expect(audio.volume).toBe(100);

  spy.mockRestore();
});

jest.clearAllMocks()

清除所有模拟的 mock.callsmock.instances 属性。相当于在每个模拟函数上调用 .mockClear()

返回用于链接的 jest 对象。

jest.resetAllMocks()

重置所有模拟状态,相当于在每个模拟函数上调用 .mockReset()

返回用于链接的 jest 对象。

jest.restoreAllMocks()

将所有模拟恢复到其原始值。相当于在每个模拟函数上调用 .mockRestore() 。请注意 jest.restoreAllMocks() 仅在使用 jest.spyOn 创建模拟时有效;其他模拟则需要手动恢复它们。

Mock timers

jest.useFakeTimers(implementation?: 'modern' | 'legacy')

指示 Jest 使用标准计时器函数的伪造版本( setTimeoutsetIntervalclearTimeoutclearIntervalnextTicksetImmediateclearImmediate 以及 Date)。

如果你将 'legacy' 作为参数传递,则将使用 Jest 的遗留的实现,而不是基于 @sinonjs/fake-timersopen in new window 的实现。

返回用于链接的 jest 对象。

jest.useRealTimers()

指示 Jest 使用标准计时器函数的真实版本。

返回用于链接的 jest 对象。

jest.runAllTicks()

耗尽微任务队列(通常通过节点连接到 process.nextTick)。

调用此 API 时,所有通过 process.nextTick 排队的待处理微任务都将被执行。此外,如果这些微任务自己调度新的微任务,这些微任务将不断执行,直到队列中没有更多的微任务。

jest.runAllTimers()

耗尽宏任务队列(即所有由 setTimeout()setInterval()setImmediate() 队列中的任务)和微任务队列(通常通过节点连接到 process.nextTick)。

调用此 API 时,将执行所有未完成的宏任务和微任务。如果这些任务自己调度了新任务,那么这些任务将不断执行,直到队列中没有剩余任务为止。

这对于在测试期间同步执行 setTimeouts 通常很有用,以便同步某些仅在 setTimeout()setInterval() 回调后才会发生的行为。更多信息,请参阅计时器模拟open in new window文档。

jest.runAllImmediates()

耗尽 setImmediate() 队列中的所有任务。

注意:此功能不可用与使用现代伪定时器的实现

jest.advanceTimersByTime(msToRun)

仅执行宏任务队列(即由 setTimeout()setInterval()setImmediate() 队列中的所有任务)。

调用此 API 时,所有计时器都会提前 msToRun 毫秒。所有已通过 setTimeout()setInterval() 排队并在此时间范围内未完成执行的“宏任务”都将被执行。此外,如果这些宏任务在同一时间范围内执行的新宏任务,则这些宏任务将一直执行,直到队列中不再有宏任务为止,但这些都在 msToRun 毫秒内运行。

jest.runOnlyPendingTimers()

仅执行当前挂起的宏任务(即仅执行到目前为止已由 setTimeout()setInterval() 排队的任务)。如果当前挂起的宏任务调度新的宏任务,则并不会执行这些新任务。

这对于被测试模块调用 setTimeout() 的场景很有用,当其回调以递归方式调用另一个 setTimeout()(意味着调度永不停止)。在这情况下,能够一次只向前运行一次是很有用的。

jest.advanceTimersToNextTimer(steps)

通过将所有计时器所需的时间提前,以便仅运行下一个 超时/间隔。

或者,你可以提供 steps,因此它将运行下一个 超时/间隔 的 steps 数量。

jest.clearAllTimers()

从计时器系统中删除任何已挂起的计时器。

这意味着,如果任何计时器已被调度(但尚未执行),它们将被清除并且将来永远不会执行。

jest.getTimerCount()

返回仍要运行的假计时器的数量。

jest.setSystemTime(now?: number | Date)

设置假定时器使用的当前系统时间。模拟用户在程序运行时更改系统时钟的操作。它会影响当前时间,但它本身不会导致定时器触发;将会完全按照不调用 jest.setSystemTime() 的方式进行触发。

注意:此功能仅在使用现代伪计时器实现时可用

jest.getRealSystemTime()

当模拟时间时,Date.now() 也会被模拟。如果你出于某种原因需要访问实时当前时间,你可以调用此方法。

注意:此功能仅在使用现代伪计时器实现时可用

Misc

jest.setTimeout(timeout)

以毫秒为单位设置测试和挂钩之前/之后的默认超时间隔。这仅影响调用此方法的测试文件。

注意:如果不调用此方法,默认超时间隔为 5 秒。

注意:如果你想为所有测试文件设置超时,setupFilesAfterEnv 是一个好方法。

jest.setTimeout(1000); // 1s

jest.retryTimes()

运行失败的测试 n 次,直到它们通过或用尽最大重试次数。这仅适用于默认的 jest-circusopen in new window 运行。

测试示例:

jest.retryTimes(3);
test("will fail", () => {
  expect(true).toBe(false);
});

返回用于链接的 jest 对象。

上一章-Mock Functions

下一章-Configuring Jest

上次编辑于:
贡献者: zddbic