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

使用axios-mock-adapter测试时POST请求报404错误排查

问题:Axios Mock Adapter POST请求测试返回404,GET请求正常

在Vue项目中使用Vitest运行单元测试时,通过axios-mock-adapter测试自定义Axios实例,GET请求测试能通过,但POST请求始终返回Request failed with status code 404错误。

原Axios实例代码(default.js)

import axios from "axios";

const BASE_URL = import.meta.env.VITE_BACKEND_BASE_URL;

const instance = axios.create({
  baseURL: BASE_URL,
});

instance.defaults.headers.common["Accept"] = "application/json";
instance.defaults.headers.post["Content-Type"] = "application/json";

instance.interceptors.request.use(
  function (config) {
    config.headers.Authorization = localStorage.getItem("token");
    return config;
  },
  function (error) {
    return Promise.reject(error);
  }
);

export default instance;

原单元测试代码(default.test.js)

import MockAdapter from "axios-mock-adapter";
import axios from "@/axios/default";

describe("Axios functions", () => {
  let mock;

  beforeEach(() => {
    mock = new MockAdapter(axios);
  });

  it("sends a GET request to the endpoint", async () => {
    const endpoint = "/example";
    const url = import.meta.env.VITE_BACKEND_BASE_URL + endpoint;
    const token = "12345";
    const expectedData = { example: "data" };

    mock
      .onGet(url, {
        headers: { Authorization: token, Accept: "application/json" },
      })
      .reply(200, expectedData);
    localStorage.setItem("token", token);

    const response = await axios.get(endpoint);

    expect(mock.history.get.length).toBe(1);
    expect(mock.history.get[0].url).toBe(endpoint);
    expect(mock.history.get[0].headers.Authorization).toBe(token);
    expect(mock.history.get[0].headers.Accept).toBe("application/json");
    expect(response.data).toEqual(expectedData);
  });

  it("sends a POST request to the endpoint", async () => {
    const endpoint = "/example";
    const url = import.meta.env.VITE_BACKEND_BASE_URL + endpoint;
    const token = "12345";
    const postData = { example: "data" };
    const expectedData = { success: true };

    mock
      .onPost(url, postData, {
        headers: {
          Authorization: token,
          "Content-Type": "application/json",
          Accept: "application/json",
        },
      })
      .reply(200, expectedData);
    localStorage.setItem("token", token);

    const response = await axios.post(endpoint, postData);

    expect(mock.history.post.length).toBe(1);
    expect(mock.history.post[0].url).toBe(endpoint);
    expect(mock.history.post[0].headers.Authorization).toBe(token);
    expect(mock.history.post[0].headers["Content-Type"]).toBe(
      "application/json"
    );
    expect(mock.history.post[0].headers.Accept).toBe("application/json");
    expect(JSON.parse(mock.history.post[0].data)).toEqual(postData);
    expect(response.data).toEqual(expectedData);
  });
});

报错原因分析

  1. URL匹配冗余:自定义Axios实例已配置baseURL,调用axios.post(endpoint)时,mock适配器会自动拼接baseURL,无需手动拼接完整URL进行匹配,否则会导致URL不匹配。
  2. POST数据匹配问题:Axios会自动将POST请求的对象数据序列化为JSON字符串,但mock时直接传入原始对象postData,导致适配器无法匹配到对应的请求。
  3. Headers严格匹配冲突:onPost的第三个参数是严格匹配headers,但Axios可能会自动添加额外的headers字段,或者大小写处理差异,导致匹配失败。

解决方法及修改后的测试代码

针对上述问题,调整POST测试的mock逻辑:

  • 直接使用endpoint作为mock的URL,无需拼接baseURL
  • 用JSON.stringify(postData)匹配序列化后的请求数据,或使用函数校验数据
  • 简化headers匹配,只验证关键的Authorization字段,避免严格匹配所有headers

修改后的测试代码:

import MockAdapter from "axios-mock-adapter";
import axios from "@/axios/default";

describe("Axios functions", () => {
  let mock;

  beforeEach(() => {
    mock = new MockAdapter(axios);
  });

  it("sends a GET request to the endpoint", async () => {
    const endpoint = "/example";
    const token = "12345";
    const expectedData = { example: "data" };

    // 直接使用endpoint匹配,无需拼接baseURL
    mock
      .onGet(endpoint, {
        headers: { Authorization: token }
      })
      .reply(200, expectedData);
    localStorage.setItem("token", token);

    const response = await axios.get(endpoint);

    expect(mock.history.get.length).toBe(1);
    expect(mock.history.get[0].url).toBe(endpoint);
    expect(mock.history.get[0].headers.Authorization).toBe(token);
    expect(mock.history.get[0].headers.Accept).toBe("application/json");
    expect(response.data).toEqual(expectedData);
  });

  it("sends a POST request to the endpoint", async () => {
    const endpoint = "/example";
    const token = "12345";
    const postData = { example: "data" };
    const expectedData = { success: true };

    // 直接使用endpoint,匹配序列化后的data,简化headers匹配
    mock
      .onPost(endpoint, JSON.stringify(postData), {
        headers: { Authorization: token }
      })
      .reply(200, expectedData);
    localStorage.setItem("token", token);

    const response = await axios.post(endpoint, postData);

    expect(mock.history.post.length).toBe(1);
    expect(mock.history.post[0].url).toBe(endpoint);
    expect(mock.history.post[0].headers.Authorization).toBe(token);
    expect(mock.history.post[0].headers["Content-Type"]).toBe(
      "application/json"
    );
    expect(mock.history.post[0].headers.Accept).toBe("application/json");
    expect(JSON.parse(mock.history.post[0].data)).toEqual(postData);
    expect(response.data).toEqual(expectedData);
  });
});

另一种更灵活的方式是使用函数校验POST数据,避免序列化问题:

// 替换POST的mock部分
mock
  .onPost(endpoint, (data) => JSON.parse(data) === postData, {
    headers: { Authorization: token }
  })
  .reply(200, expectedData);

内容的提问来源于stack exchange,提问作者Suyash Mittal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:37:17