【问题标题】:Testing: mocking node-fetch dependency that it is used in a class method测试:模拟在类方法中使用的 node-fetch 依赖项
【发布时间】:2017-10-13 03:01:42
【问题描述】:

我有以下情况:

A.js

import fetch from 'node-fetch'
import httpClient from './myClient/httpClient'

 export default class{
    async init(){
       const response = await fetch('some_url')
       return httpClient.init(response.payload)
    }
}

A_spec.js

import test from 'ava'
import sinon from 'sinon'
import fetch from 'node-fetch'
import httpClient from './myClient/httpClient'
import A from './src/A'

test('a async test', async (t) => {
    const instance = new A()
    const stubbedHttpInit = sinon.stub(httpClient, 'init')
    sinon.stub(fetch).returns(Promise.resolve({payload: 'data'})) //this doesn't work

    await instance.init()
    t.true(stubbedHttpInit.init.calledWith('data'))
})

我的想法是检查 httpClient 的 init 方法是否已使用获取请求中获得的有效负载调用。

我的问题是:当我测试 A 的 init 方法时,如何模拟 fetch 依赖项以存根返回的值?

【问题讨论】:

    标签: testing sinon stub ava


    【解决方案1】:

    最后我解决了这个问题,像这样存根 fetch.Promise 引用:

    sinon.stub(fetch, 'Promise').returns(Promise.resolve(responseObject))
    

    对此的解释是 node-fetch 引用了原生 Promise,当您调用 fetch() 时,此方法返回 fetch.PromiseCheck this out

    【讨论】:

    • 这不适用于打字稿。当我尝试编写上述确切代码时出现以下错误:“Promise”类型的参数不可分配给“isRedirect”类型的参数。
    • Tocayo:实际的 responseObject 是什么?似乎您的参考链接不再准确到您的意思,因为第 37 行只是一个空行
    【解决方案2】:

    sinon.stub(fetch) 不能对函数本身存根。相反,您需要从./src/A 内部存根node-fetch 依赖项,可能使用类似proxyquire 的东西:

    import proxyquire from 'proxyquire`
    const A = proxyquire('./src/A', {
      'node-fetch': sinon.stub().returns(Promise.resolve({payload: 'data'}))
    })
    

    【讨论】:

    • 感谢您的评论,但最后我在没有外部依赖的情况下解决了这个问题。检查我的答案。
    【解决方案3】:

    你可以存根fetch() 描述为in the manual

    import sinon from 'sinon'
    import * as fetchModule from 'node-fetch'
    import { Response } from 'node-fetch'
    // ...
    const stub = sinon.stub(fetchModule, 'default')
    stub.returns(new Promise((resolve) => resolve(new Response(undefined, { status: 401 }))))
    // ...
    stub.restore()
    

    请注意,fetch()node-fetch 的默认导出,因此您需要存根 default

    【讨论】:

      【解决方案4】:

      就我而言,我发现保留node-fetch 功能很有用,因为我的模拟响应已经通过nock 提供

      为了实现这一点,我代理了上述答案中描述的依赖项,但在间谍中包装了一个 require 调用:

      import proxyquire from 'proxyquire'
      import { spy } from 'sinon'
      
      const fetchSpy = spy(require('node-fetch'))
      const moduleA = proxyquire(
        './moduleA',
       { 'node-fetch': fetchSpy }
      )
      

      ...

      expect(fetchSpy.args).toBe(...)
      

      【讨论】:

        【解决方案5】:

        @mrtnlrsn 的答案不适用于 NodeJS 中从 TypeScript 生成的文件,因为default 是一个生成的属性,每个导入此类依赖项的模块都有自己的,因此存根不会影响其他模块。

        然而,NodeJS 允许访问导入的模块,所以存根是这样工作的:

        const nodeFetchPath = require.resolve("node-fetch");
        const nodeFetchModule = require.cache[nodeFetchPath];
        assert(nodeFetchModule);
        const stubNodeFetch = sinon.stub(nodeFetchModule, "exports");
        

        确保您使用与测试模块相同的 node-fetch 模块,例如使用yarn dedupe。或者建立你自己的nodeFetchPath

        【讨论】:

          猜你喜欢
          • 2012-03-20
          • 2020-12-06
          • 2015-07-14
          • 2016-11-12
          • 1970-01-01
          • 2016-02-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多