模拟和测试require.resolve以开玩笑的方式调用

时间:2017-12-20 13:44:06

标签: unit-testing jestjs

我想在jest测试套件中模拟节点的内置函数require.resolve。这是一个问题on repl.it和代码本身的工作示例:

测试对象(作为示例):

const requirer = () => {
  try {
    return require.resolve('./add')
  } catch (error) {
    console.error('failed to find a module')
  }
}

module.exports = requirer

测试套件:

const requirer = require('./requirer')

describe('attempt to mock require.resolve', () => {
   it('does not work', () => {
     require.resolve = jest.fn(arg => `./${arg}`)
     console.log(
       'is require.resolve mocked',
       jest.isMockFunction(require.resolve)) // will say true

     requirer()

     expect(require.resolve).toHaveBeenCalledTimes(1)
     expect(require.resolve).toBeCalledWith('')
  })
})

在测试套件声明中,一切正常(参见jest.isMockFunction(require.resolve)的输出)并且模拟工作。但是对于测试主题require.resolve仍然具有原始功能。

由于这个问题,这不是纯粹的单元测试。

例如,如果我,模拟process.exit一切都按预期工作。

2 个答案:

答案 0 :(得分:0)

所以,不是一个完美的解决方案,但注入(DI)require.resolve到

更简单
const requirer = (resolver) => {
  try {
    return resolver('./add')
  } catch (error) {
    console.error('failed to find a module')
  }
}

module.exports = requirer

现在在测试套件中传递模拟版本的require.resolve按预期工作

const requirer = require('./ requirer')

describe('attempt to mock require.resolve', () => {
   it('works', () => {
     require.resolve = jest.fn(arg => `./${arg}`)
     console.log(
       'is require.resolve mocked',
       jest.isMockFunction(require.resolve)) // will say true

     requirer(require.resolve)

     expect(require.resolve).toHaveBeenCalledTimes(1)
     // expect(require.resolve).toBeCalledWith('')
  })
})

答案 1 :(得分:0)

我知道这并不是您真正要的东西,但这是我的解决方法,可以“模拟” require.resolve。 jest中有一个有趣的功能,可让您定义自己的模块解析器: https://jestjs.io/docs/en/configuration#resolver-string

记住这一点,基本上就是我所做的:

  • 我这样创建了自己的解析器:
const glob = require('glob');

let mapping = {};

// Looks for "module-resolution.json" files in all the `__tests__` directories
glob
  .sync(`${__dirname}/../../packages/**/src/**/__tests__/modules-resolution.json`)
  .forEach((file) => {
    // For each of them, merges them in the "mapping" object
    mapping = { ...mapping, ...require(file) };
  });

function resolver(path, options) {
  // If the path corresponds to a key in the mapping object, returns the fakely resolved path
  // otherwise it calls the Jest's default resolver
  return mapping[path] || options.defaultResolver(path, options);
}

module.exports = resolver;
  • 然后,我在玩笑的配置文件中对其进行了配置:
module.exports = {
  roots: ['<rootDir>'],
  testMatch: ['<rootDir>/packages/**/__tests__/**/*.test.js'],
  collectCoverageFrom: ['packages/**/src/**/*.js', '!packages/**/src/__tests__/**'],
  resolver: '<rootDir>/test-utils/resolver',
};
  • 最后,我可以在测试文件夹中创建modules-resolution.json文件,如下所示:
{
  "fake-module": "/path/to/fake-module"
}

到目前为止,这已经为我完成了工作,我认为,从本示例开始,我们可以做一些更复杂但对开发人员更友好的事情! 当然,如果此功能可以直接包含在jest中,那就更好了! 无论如何,我希望这对你们中的某些人有帮助?