如何在 JEST 中测试并行模拟数据请求,同时模拟具有 500 毫秒阈值的缓存响应

rag*_*hes 6 javascript caching node.js async-await jestjs

测试的目的是模拟获取不同数据源的并行请求。我为每个请求引入了人为延迟,一段时间后返回一个带有标识数字的简单字符串,以查看数据是否已从缓存中加载(500 毫秒内的请求)。因此,对于 500 毫秒内加载的数据,输出应为“A1B1”,否则,500 毫秒后应为“A2B2”,依此类推。

// index.test.js
const { wait } = require('./util/wait.js');
const { requestAandB, requestBandC } = require('./index.js');

test('Test cache timings ', () => Promise.all([

  // send two requests in parallel after 0 ms (immediately)
  wait(0).then(() => Promise.all([
    expect(requestAandB()).resolves.toEqual('A1B1'),
    expect(requestBandC()).resolves.toEqual('B1C1'),
  ])),

  // send two requests in parallel after 480 ms
  wait(480).then(() => Promise.all([
    expect(requestAandB()).resolves.toEqual('A1B1'),
    expect(requestBandC()).resolves.toEqual('B1C1'),
  ])),

  // send two requests in parallel after 520 ms
  wait(520).then(() => Promise.all([
    expect(requestAandB()).resolves.toEqual('A2B2'),
    expect(requestBandC()).resolves.toEqual('B2C2'),
  ])),

]));
Run Code Online (Sandbox Code Playgroud)

这就是我模拟数据加载的方式

// get-data.js

async function mockLoading(str) {
  // introduce some latency
  const waitDuration = Math.round(Math.random() * (WAIT_MAX - WAIT_MIN)) + WAIT_MIN;
  await wait(waitDuration);
  // create or increase counter every time the function is being called
  counters[str] = counters[str] === undefined ? 1 : counters[str] + 1;
  return str + counters[str];
}

module.exports = {
  loadDataA: async () => mockLoading('A'),
  loadDataB: async () => mockLoading('B'),
  loadDataC: async () => mockLoading('C'),
}
Run Code Online (Sandbox Code Playgroud)

最后,在测试文件中导入的方法requestAandB和实现requestBandC

const { loadDataA, loadDataB, loadDataC } = require('./model/get-data.js');
    
const all = Promise.all([loadDataA(), loadDataB(), loadDataC()])

function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms))
}  
   
async function requestAandB() {
  const temp = await all

  await delay(Math.random() * 510)
 
  return temp.filter((_, i) =>  i < 2).join("")

}

async function requestBandC() {
  const temp = await all
 
  await delay(Math.random() * 510)

  return temp.filter((_, i) => i > 0).join("") 

}

module.exports = { requestAandB, requestBandC }
Run Code Online (Sandbox Code Playgroud)

对数据“A1B1”、“B1C1”的测试很好,但由于延迟(在mockLoading函数中)始终高于 500 毫秒阈值,因此我无法获得此后返回的数据的正确结果。实际上,“A2B2”和“B2C2”总是失败。

有谁知道我在这里错过了什么?

rag*_*hes 1

这确实是一个棘手的问题,但我设法通过执行以下操作来解决它:

  1. 移动Promise.all里面各自的功能;这是因为 loadData 函数的承诺仅在模块加载时调用一次(感谢 @Teneff 和 @Observer 的提示)

  2. 实现缓存函数来缓存预期的承诺(不是预期的结果...),一旦满足某些条件就返回 loadData (缓存)。

mockLoading我们根本不需要改变这个功能。OP 需要实现客户端代码,然后调用外部 API 来模拟n个异步请求之间的高方差。换句话说,我们必须消除(或最小化)并行异步请求的数据冲突风险,以及如何在 Jest 中测试一些模拟案例。

一些代码使整个过程更加清晰:

const { loadDataA, loadDataB, loadDataC } = require('./model/get-data.js');

// we tackle this problem using memoization
// when `useCache` is called, we cache the loadData function name
// we then assign 1. a timestamp property to it and 2. a pointer to the promise itself

const CACHE_DURATION = 500;

const cache = {};
 function useCache(fn) {
   const cached = cache[fn.name];
   const now = Date.now();
   if (!cached || cached.ts < now - CACHE_DURATION) {
     cache[fn.name] = { ts: now, promise: fn() }
   }
   return cache[fn.name].promise;
 }
 
 async function requestAandB() {
   const [a, b] = await Promise.all([ loadDataA, loadDataB ].map(useCache));
   return a + b;
 }
 
 async function requestBandC() {
   const [b, c] = await Promise.all([ loadDataB, loadDataC ].map(useCache));
   return b + c;
 }

module.exports = { requestAandB, requestBandC }
Run Code Online (Sandbox Code Playgroud)

谢谢