Jest.js 只模拟模块中的一个函数,其余的保留原有的功能

kxxlusnw  于 12个月前  发布在  Jest
关注(0)|答案(6)|浏览(171)

我只想从模块中模拟一个函数(名为export),但保持模块的其余函数不变。
使用jest.mock('package-name')会使所有导出的函数都成为mock,这是我不希望看到的。
我试着将指定的导出扩展回mock对象.

import * as utils from './utilities.js';

jest.mock(utils, () => ({
  ...utils
  speak: jest.fn(),
}));

字符串
但得到了这个错误:
jest.mock()的模块工厂不允许引用任何范围外的变量。

mrphzbgm

mrphzbgm1#

这个答案的亮点是jest.requireActual(),这是一个非常有用的实用程序,它说:“嘿,保持每个原始功能的完整性并导入它们”。

jest.mock('./utilities.js', () => ({
  ...jest.requireActual('./utilities.js'),
  speak: jest.fn(),
}));

字符串
让我们来看看另一个常见的场景,你正在使用酶ShallowWrapper,它与useContext()钩子不匹配,所以你要做什么?虽然我相信有多种方法,但这是我喜欢的一种:

import React from "react";

jest.mock("react", () => ({
  ...jest.requireActual("react"), // import and retain the original functionalities
  useContext: jest.fn().mockReturnValue({foo: 'bar'}) // overwrite useContext
}))


这样做的好处是,您仍然可以在原始代码中使用import React, { useContext } from "react",而不必担心将它们转换为React.useContext(),就像您使用**jest.spyOn(React,'useContext')**一样。

gg0vcinb

gg0vcinb2#

最直接的方法是先使用jest.spyOn,然后再使用.mockImplementation()。这将允许模块中的所有其他函数继续按照它们的定义工作。
对于包裹:

import axios from 'axios';

jest.spyOn(axios, 'get');
axios.get.mockImplementation(() => { /* do thing */ });

字符串
对于具有命名导出的模块:

import * as utils from './utilities.js';

jest.spyOn(utils, 'speak');
utils.speak.mockImplementation(() => { /* do thing */ });


网址:https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname

vatpfxk5

vatpfxk53#

jest.mock内部的jest.requireActual似乎是要走的路,但是我需要添加一个代理而不是对象扩展,以防止在某些导入场景中可能发生的类型错误Cannot read properties of undefined (reading ...)
这是最后的结果:

jest.mock('the-module-to-mock', () => {
  const actualModule = jest.requireActual('the-module-to-mock')

  return new Proxy(actualModule, {
    get: (target, property) => {
      switch (property) {
        // add cases for exports you want to mock
        // 👇👇👇
        case 'foo': {
          return jest.fn() // add `mockImplementation` etc
        }
        case 'bar': {
          return jest.fn()
        }
        // fallback to the original module
        default: {
          return target[property]
        }
      }
    },
  })
})

字符串

5lwkijsr

5lwkijsr4#

对我来说,这是有效的:

const utils = require('./utilities.js');
...
jest.spyOn(utils, 'speak').mockImplementation(() => jest.fn());

字符串

czq61nw1

czq61nw15#

我接受了Rico Kahler的答案,并创建了这个通用函数:

function mockPartially(packageName: string, getMocks: (actualModule: any) => any) {
  jest.doMock(packageName, () => {
    const actualModule = jest.requireActual(packageName);
    const mocks = getMocks(actualModule);

    return new Proxy(actualModule, {
      get: (target, property) => {
        if (property in mocks) {
          return mocks[property];
        } else {
          return target[property];
        }
      },
    });
  });
}

字符串
你可以这样使用它来模拟lodash:

mockPartially('lodash', (_actualLodash) => { //sometimes you need the actual module
   return {
      'isObject': () => true, //mock isObject
      'isArray': () => true // mock isArray
   }
});

laximzn5

laximzn56#

我建议考虑设置mock,以便它继续调用原始函数,直到mock被显式重置,并在对被模拟的函数感兴趣的测试之前设置所需的mock行为。

jest.mock("fs", () => {
  const originalFs = jest.requireActual("fs");

  return {
    __esModule: true,
    ...originalFs,
    // call original by default until specific tests as this avoids breaking
    // any imports that might perform a readFileSync when loading.
    readFileSync: jest.fn().mockImplementation((args) => originalFs.readFileSync(args)),
  };
});

字符串
为了解释为什么,大多数答案在大多数情况下都有效,但是对于核心或外部库中的任何模块,如果被多个其他导入使用,它们可能会产生副作用。
当你使用jest.mock("<name>")时,它会在实际执行导入之前应用。查看https://jestjs.io/docs/mock-functions#mocking-modules你可以看到mock是在导入之后,但它仍然按照预期工作。然而在https://jestjs.io/docs/bypassing-module-mocks中,mock是在导入之前创建的,这可能并不明显,即使后来的jest hook足够早,可以在处理导入中的内容之前应用mock。
这意味着任何被导入并试图创建一个对象的具体示例以设置为模块属性的东西都将获得模拟示例而不是原始示例。
在某些情况下,这可能是预期的,但更有可能是令人惊讶的。通过最初将mock传递到原始调用,它允许其他导入初始设置使用该函数,然后可以在测试调用它的函数之前切换到使用所需的mock实现。
显然,如果你在模仿你写的东西,这就不那么重要了,更多的是在使用库和框架时,这些库和框架可能有你可能没有完全意识到的依赖关系和行为。
在我的例子中,我被GitHub的@actions/toolkit中的上下文对象咬了一口,希望在加载时读取JSON文件,这只会在设置GITHUB_EVENT_PATH env var时引起问题。https://github.com/actions/toolkit/blob/main/packages/github/src/context.ts

相关问题