You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用axios-retry时nock无法拦截后续请求的解决方法

Nock与axios-retry协同工作的问题与解决方案

问题概述

  • 正常请求场景下,nock可以正常拦截并模拟API响应
  • 触发axios-retry的重试逻辑时,首次重试请求就会触发Nock匹配失败,日志提示无匹配的请求规则

相关代码与测试信息

客户端请求代码

const getClient = (authorizationToken: string | undefined, target: ApiParams) =>
  axios.create({
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
      ...(!!authorizationToken && { Authorization: `Bearer ${authorizationToken}` }),
      'x-api-key': target.apiKey || 'missing',
    },
    timeout: API_TIMEOUT_MILLISECONDS,
  });

export const sendRequest = ({ someParams }) => {
  const client = getClient(authorizationToken, target);

  axiosRetry(client, {
    retries: API_MAX_RETRIES,
    retryDelay: (retryCount) => {
      logger.info('retrying request', retryCount);
      return retryCount * API_RETRY_INTERVAL_MS;
    },
    retryCondition: (error) => {
      // 其他逻辑已省略
      return Number(error.response.status) >= 500;
    },
  });
}

待测试的业务逻辑

export const upsertExclusionList = async () => {
  const response: ApiClientResult = sendRequest({ someParams });
  if (response.data) {
    return response.data;
  } else {
    throw Error('No data');
  }
}

正常工作的测试用例

it('processes valid request', async () => {
  nock(serverBaseUrl)
    .put('/exclusion-lists', { some: 'data' })
    .query({ callerId: 'Tester', role: 'Tester' })
    .reply(200, { success: true });
  const response: ExclusionList = await upsertExclusionList({ someParams });
  expect(response).toEqual({ some: 'thing' });
});

触发重试的失败测试用例

it('handles a 500 response from the API', async () => {
  nock(serverBaseUrl)
    .put('/exclusion-lists', { some: 'data' })
    .query({ callerId: 'Tester', role: 'Tester' })
    .reply(500, { error: 'Some error' });
  await expect(upsertExclusionList({ someParams }).toBeCalledTimes(4);
  // 也曾尝试:
  // await expect(upsertExclusionList({ someParams }).rejects.toEqual('No data');
});

实际报错信息

Attempted to log "Call to put exclusion list API (https://api.example.com/exclusion-lists?callerId=Tester&role=Tester) failed: Nock: No match for request {
  "method": "PUT",
  "url": "https://api.example.com/exclusion-lists?callerId=Tester&role=Tester",
  "headers": {
    "accept": "application/json",
    "content-type": "application/json",
    "authorization": "Bearer unit-test",
    "x-api-key": "xxx",
    "user-agent": "axios/1.2.0-alpha.1",
    "content-length": "93",
    "accept-encoding": "gzip, deflate, br"
  },
  "body": "{\"some\":\"data\"}"
} (2080 ms)".

依赖版本

node 18.13.0

"axios-retry": "3.3.1",
"nock": "13.3.0",
"jest": "28.1.3",
"ts-jest": "28.0.8",
"typescript": "4.3.5"

解决方案

Nock支持重试场景,核心问题是默认每个拦截器仅匹配一次请求,而axios-retry会发起多次重复请求,导致后续请求无匹配规则。以下是具体配置步骤:

1. 指定Nock拦截器的匹配次数

在测试用例中,给Nock拦截器添加times(n)方法,n为总请求次数(初始请求1次 + 重试次数)。比如你设置了retries: 3,则总请求数是4次:

it('handles a 500 response from the API', async () => {
  // 匹配4次请求(1次初始 + 3次重试)
  nock(serverBaseUrl)
    .put('/exclusion-lists', { some: 'data' })
    .query({ callerId: 'Tester', role: 'Tester' })
    .times(4)
    .reply(500, { error: 'Some error' });

  // 验证业务逻辑抛出预期错误
  await expect(upsertExclusionList({ someParams })).rejects.toThrow('No data');
  // 验证所有请求都被Nock匹配
  expect(nock.isDone()).toBe(true);
});

如果不需要精确控制次数,也可以用persist()让拦截器持续匹配直到测试结束,但times()更适合明确重试次数的场景:

nock(serverBaseUrl)
  .put('/exclusion-lists', { some: 'data' })
  .query({ callerId: 'Tester', role: 'Tester' })
  .persist()
  .reply(500, { error: 'Some error' });

2. 修正测试断言逻辑

原测试用例中的toBeCalledTimes(4)是错误用法,该方法用于验证函数被调用的次数,而非请求次数。正确的做法是:

  • 验证业务逻辑是否抛出预期错误
  • 用nock.isDone()确认所有请求都被Nock拦截匹配

3. 确保请求参数完全匹配

如果仍出现匹配失败,检查请求的headers、body、URL是否与Nock规则完全一致:

  • 比如报错中出现的user-agent、accept-encoding等headers,Nock默认会严格匹配,可通过matchHeader宽松匹配:
    nock(serverBaseUrl)
      .put('/exclusion-lists', { some: 'data' })
      .query({ callerId: 'Tester', role: 'Tester' })
      .matchHeader('user-agent', /.*/) // 匹配任意user-agent
      .matchHeader('accept-encoding', /.*/)
      .times(4)
      .reply(500, { error: 'Some error' });
    
  • 确保请求body的序列化格式与Nock预期一致(比如是否有多余的空格、字段顺序问题)

内容的提问来源于stack exchange,提问作者Juha Untinen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 13:35:18