【问题标题】:How to write unit test for exponential backoff retry method implemented in javascript with jest如何用 jest 为在 javascript 中实现的指数退避重试方法编写单元测试
【发布时间】:2020-12-02 10:52:05
【问题描述】:

尝试测试通过 fetch 重试 API 请求 5 次的指数退避方法,将有以下延迟:[1 ms, 10 ms, 100 ms, 1 s, 10 s],我无法成功测试它.

方法

export const delay = retryCount => new Promise(resolve => setTimeout(resolve, 10 ** retryCount));

/**
 * Fetching with delay when api call fails,
 * first 5 retries will have the following delays: [1 ms, 10 ms, 100 ms, 1 s, 10 s]
 */
export const fetchRetry = async (options, retryCount = 0, lastError = null) => {
  if (retryCount > 5) throw new Error(lastError);
  try {
    return await fetch(options);
  } catch (error) {
    await delay(retryCount);
    return fetchRetry(options, retryCount + 1, error);
  }
};

测试

import fetchMock from 'jest-fetch-mock';

import { delay, fetchRetry } from './retry';

// This can be set up globally if needed
fetchMock.enableMocks();

beforeEach(() => {
  fetch.resetMocks();
});

describe('fetchWithExponentialBackoffRetry', () => {
  it('fetch is called once when response is 200', done => {
    fetch.mockResponseOnce(
      JSON.stringify({
        success: true,
        message: 'OK',
        code: 200,
        data: 'c86e795f-fe70-49be-a8fc-6876135ab109',
      }),
    );

    setTimeout(function() {
      fetchRetry({
        inventory_type_id: 2,
        advertiser_id: 2315,
        file: null,
      });
      expect(fetch).toHaveBeenCalledTimes(1);
      done();
    }, 0);
  });

  it('fetch is called 5 times when response is returns failure', done => {
    fetch.mockReject(() => Promise.reject(new Error('Rejected')));

    setTimeout(function() {
      fetchRetry({
        inventory_type_id: 2,
        advertiser_id: 2315,
        file: null,
      });
      expect(fetch).toHaveBeenCalledTimes(5);
      done();
    }, 100000);
  });
});

我收到以下错误

console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29 错误:错误:连接 ECONNREFUSED 127.0.0.1:8

我认为它必须做我们delay 方法我必须在我的测试中以某种方式合并 setTimeout,现在确定如何在这里模拟它。我将不胜感激。

【问题讨论】:

  • 你没有在第一次测试中等待fetchRetry
  • 我更新了测试,jest sill 超时,我看到了上面提到的错误。我将此测试的超时时间增加到 10000,但仍然会在一段时间后超时
  • 我进行了一些编辑并使用了 setTimeout,但测试仍然失败。不知道现在该怎么办

标签: javascript jestjs


【解决方案1】:

您正在测试异步函数的结果,因此您也需要使测试异步 - 您没有这样做 - 即您没有等待 fetchRetry,因此只是同步调用 done()

认为错误是由此处使用setTimeout 引起的。这看起来像是一个竞争条件错误,因此很难在没有调试的情况下确定,但从阅读代码来看,问题似乎是你在用jest-fetch-mock 模拟fetch,但是因为你的测试代码同步运行并且你有。 ..

beforeEach(() => {
  fetch.resetMocks();
});

...它可能在调用首先运行的测试之前取消设置 fetch 模拟,因此它实际上是在调用您的 API - 因此出现错误。

使测试异步相当简单 - docs are here - 使用 async/await 更简洁,因为您实际上不需要使用 done - 当承诺解决(或拒绝)时测试才完成.

基本上,您的测试代码将大体相同,除了您将 awaiting 您对 fetchRetry 的调用,如下所示:

it('fetch is called once when response is 200', async () => {
  fetch.mockResponseOnce(...)
     
  await fetchRetry({ ... }) 
  expect(fetch).toHaveBeenCalledTimes(1);
});

it('fetch is called 5 times when response is returns failure', async () => {
  fetch.mockReject(...);

  try {
    await fetchRetry({ ... });
  } catch (err) {
    // eventual error expected as response failure is mocked
    expect(fetch).toHaveBeenCalledTimes(5);  
  }  
});

【讨论】:

  • 这应该导致测试失败,不是吗?存在未处理的拒绝承诺。
  • 它返回一个承诺,因为async 是返回承诺的函数的语法糖,而awaitthen 的语法糖。如果fetchRetry() 导致 promise 被拒绝(很可能是 fetch.mockReject 的情况),则测试失败。
  • 啊,抱歉,快速阅读并认为您在谈论 OP 的原始代码 :-) 是的,您是对的,由于被拒绝的承诺,该测试将失败。我想您可以将其包装在 try/catch 中以处理预期的错误。已更新答案以进行演示。
  • 这里的问题是,如果 fetchRetry 在内部捕获到错误,则断言会被跳过。我想await expect(...).rejects.toThrow(...) 适合这里。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-05
  • 2017-04-06
  • 1970-01-01
  • 2021-12-03
  • 1970-01-01
  • 2011-05-08
相关资源
最近更新 更多