如何伪造一个直接需要的功能

How to fake a directly required function

如果我需要一个函数,我如何在 sinon 中stub/mock它?

const { getLastYearData } = require('../../services')

module.exports = async lastYearInfo => {
   return getLastYearData(lastYearInfo)
}

如果我按照下面的方式操作,它可以工作,但如果不引用该对象我就无法工作。

const services = require('../../services')

module.exports = async lastYearInfo => {
   return services.getLastYearData(lastYearInfo)
}

并且:

const { expect } = require('chai')
const lastYearFunction = require('./last-year-function')
const sinon = require('sinon')
const services = require('../../services')

it('should return stubbed values', async () => {
    sandbox
     .stub(services, 'getLastYearData')
     .callsFake(lastYearInfo => {
       return { Data: 4 }
     })

   let data = await lastYearFunction({test: 1})

   const expected = { 
     Data: 4
   }

   expect(data).to.deep.equal(expected)
 })

docs 显示使用 var spy = sinon.spy(myFunc); 监视任意 myFunc

这是节点模块工作方式引入的限制。

这一行:

const lastYearFunction = require('./last-year-function')

执行 ./last-year-function.js 中的代码,进而执行此行:

const { getLastYearData } = require('../../services')

执行 ../../services.js 中的代码,并将结果的 lastYearFunction 属性 分配给模块范围内的新变量。所有这些都发生在 sinon 准备好存根之前,因此无论如何,模块中的 getLastYearData 将引用服务返回的原始函数。

虽然可以将需要 ./last-year-function 的行移动到创建存根后的某个时间,但我不建议这样做。其一,如果需要的话,不可能恢复原来的功能。然而,更糟糕的是——require 结果被缓存在 Node 中,所以任何在同一个 运行 中需要 ./last-year-function 的东西也会使用存根。或者,会使用原始的,这取决于事物的顺序 运行。它又脏又脏,所以我不推荐它。

你最好的选择实际上是按照你对第二个样本的方式来做。通过服务对象引用它,并在 afterEach 或类似的东西中恢复它。

库用于代理 require 调用,这是使您的第一个代码示例工作的唯一方法。我过去用过的一个是 proxyquire。如果需要,请尝试一下,但根据我的经验,不值得增加额外的复杂性。