首页 > 解决方案 > 使用 Jest 测试框架调用 fetch(url) 时如何根据 url 模拟变量响应?

问题描述

我有一个如下所示的模块:

计算平均.js

const fetch = require('node-fetch')
const stats = require('stats-lite')
const BASE_URL = 'https://www.example.com/api'

const calculateAverage = async(numApiCalls) => {
  const importantData = []

  for (let i = 0; i < numApiCalls; i++) {
      const url = `${BASE_URL}/${i}` // will make requests to https://www.example.com/api/0, https://www.example.com/api/1 and so on....
      const res = await fetch(url)
      const jsonRes = await res.json()

      importantData.push(jsonRes.importantData)
  }

  return stats.mean(importantData)
}

module.exports = calculateAverage

我尝试按照以下方式对其进行测试,但显然与解决方案相去甚远:

计算平均.test.js

const calculateAverage = require('../calculate-average')

jest.mock(
    'node-fetch',
    () => {
        return jest.fn(() => {})
    }
)

test('Should calculate stats for liquidation open interest delatas', async() => {
    const stats = await calculateAverage(100) // Should make 100 API calls.

    console.log(stats)
})

我需要做的是以下几点:

  1. 能够为每个 API 调用指定自定义的变化响应。例如,我应该能够指定对返回的调用、对https://www.example.com/api/0返回的{ importantData: 0 }调用等等......https://www.example.com/api/1{ importantData: 1 }
  2. url如果对我没有指定响应的请求发出请求,则会提供默认响应。例如,如果对 做出https://www.example.com/api/101响应,{ importantData: 1000 }则发送默认响应 。

我最好只使用 Jest 而不依赖于mock-fetchjest-mock-fetch. 但是,如果不使用的解决方案过于复杂,那么我很乐意使用它们。如果不需要,只是不想创建不必要的依赖项。

标签: node.jsunit-testingmockingjestjsfetch

解决方案


你当然可以!您可以使用模拟函数mockResolvedValueOnce方法返回特定调用的结果并mockResolvedValue返回默认结果。

jest.mock('node-fetch', () => {
    const generateResponse = (value) => {
        return { json: () => ({ importantData: value }) };
    };

    return jest
        .fn()
        .mockResolvedValue(generateResponse(1000)) // default response
        .mockResolvedValueOnce(generateResponse(0)) // response for first call
        .mockResolvedValueOnce(generateResponse(1)) // response for second call
        .mockResolvedValueOnce(generateResponse(2)); // response for third call
});

请注意,我们正在返回一个具有该json属性的对象,以便在您调用时返回 jsonres.json()数据calculate-average.js


如果要根据url参数返回特定响应,则必须在返回的模拟函数中模拟所需的行为node-fetch。以下示例将模拟返回的值,因此对于计数器大于 100 的 URL,它将返回 1000。否则,它将返回与 中存在的相同值url

jest.mock('node-fetch', () => {
    return jest.fn((url) => {
        // Get and parse the URL parameter.
        const value = parseInt(url.split('/').slice(-1)[0], 10);

        return Promise.resolve({
            json: () => ({ importantData: value > 100 ? 1000 : value })
        });
    });
});

推荐阅读