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

Axios拦截器TypeScript单元测试问题求助(v1.6.3)

解决Axios v1.6.3+ TypeScript环境下拦截器单元测试的正确方案

问题根源

Axios从v1.x版本开始,将interceptors.request.handlers标记为内部私有属性,TypeScript类型定义中不再对外暴露该字段,这就是你遇到类型错误的原因。早期案例能使用该方法,要么基于旧版Axios,要么是非TypeScript环境下忽略类型检查的写法,并非当前版本的最佳实践。

正确测试思路

我们不需要依赖Axios内部的handlers属性,而是通过抽离拦截器逻辑为独立函数,直接测试函数的业务逻辑;若需验证拦截器注册行为,可通过Sinon监视Axios的use方法实现。完全符合单元测试的隔离性要求,且不涉及集成测试。


步骤1:重构拦截器代码(抽离独立函数)

先把拦截器的处理逻辑从Axios实例中抽出来,方便单独测试:

// axios-interceptors.ts
import { InternalAxiosRequestConfig } from 'axios';

// 拦截器成功回调:处理token添加逻辑
export const requestSuccessHandler = (config: InternalAxiosRequestConfig) => {
  const token = localStorage.getItem('authToken');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
};

// 拦截器错误回调:处理请求错误
export const requestErrorHandler = (error: unknown) => {
  return Promise.reject(error);
};

// 实际业务中注册拦截器的函数
import axios from 'axios';
export const setupRequestInterceptor = () => {
  axios.interceptors.request.use(requestSuccessHandler, requestErrorHandler);
};

步骤2:编写单元测试(覆盖所有场景)

使用Sinon模拟依赖(如localStorage),直接测试抽离的函数,同时验证拦截器的注册行为:

// axios-interceptors.test.ts
import { expect } from 'chai';
import sinon from 'sinon';
import axios from 'axios';
import { 
  requestSuccessHandler, 
  requestErrorHandler,
  setupRequestInterceptor
} from './axios-interceptors';
import { InternalAxiosRequestConfig } from 'axios';

describe('Axios Request Interceptors', () => {
  let localStorageStub: sinon.SinonStub;

  beforeEach(() => {
    // 全局模拟localStorage.getItem方法
    localStorageStub = sinon.stub(window.localStorage, 'getItem');
  });

  afterEach(() => {
    // 恢复原方法,避免污染其他测试
    localStorageStub.restore();
  });

  // 场景1:存在token时,正确添加Authorization头
  it('adds Authorization header when token exists', () => {
    const mockToken = 'test-jwt-token';
    localStorageStub.withArgs('authToken').returns(mockToken);

    const mockConfig: InternalAxiosRequestConfig = {
      headers: {},
      method: 'get',
      url: '/api/test'
    };

    const result = requestSuccessHandler(mockConfig);
    expect(result.headers.Authorization).to.equal(`Bearer ${mockToken}`);
  });

  // 场景2:无token时,不添加Authorization头
  it('does not add Authorization header when token is missing', () => {
    localStorageStub.withArgs('authToken').returns(null);

    const mockConfig: InternalAxiosRequestConfig = {
      headers: {},
      method: 'get',
      url: '/api/test'
    };

    const result = requestSuccessHandler(mockConfig);
    expect(result.headers.Authorization).to.be.undefined;
  });

  // 场景3:错误回调正确传递错误
  it('rejects with original error in error handler', async () => {
    const mockError = new Error('Request timeout');
    await expect(requestErrorHandler(mockError)).to.be.rejectedWith(mockError);
  });

  // 验证拦截器是否被正确注册到Axios实例
  it('registers interceptors to Axios instance', () => {
    const useSpy = sinon.spy(axios.interceptors.request, 'use');
    
    setupRequestInterceptor();
    
    expect(useSpy.calledWith(requestSuccessHandler, requestErrorHandler)).to.be.true;
    useSpy.restore();
  });
});

方案优势

  1. 类型安全:完全符合TypeScript类型规范,不会出现属性不存在的错误
  2. 隔离性强:只测试拦截器的业务逻辑,不依赖Axios内部实现,避免版本更新带来的兼容问题
  3. 覆盖全面:轻松覆盖有token、无token、错误处理三个核心场景
  4. 可维护性高:拦截器逻辑与Axios注册逻辑分离,后续修改逻辑无需调整测试代码

内容的提问来源于stack exchange,提问作者Sinan Bayar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 06:45:12