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

使用Jest测试含API调用的UserModel类时Mock报错排查

问题:Mock Axios实例时出现TypeError: mockHttp.post.mockResolvedValueOnce is not a function

遇到的错误:

TypeError: mockHttp.post.mockResolvedValueOnce is not a function

测试文件代码

import { IUserApiClientUrls } from '../../../src/interfaces'
import { UserModel } from '@/models/UserModel'
import httpClient from '@/services/index'
import apiClient from '@/services/server.json'

jest.mock('@/services/index')
const mockHttp = httpClient as jest.Mocked<typeof httpClient>

const createSut = () => {
  const urlUser: IUserApiClientUrls = apiClient.user
  return new UserModel(urlUser)
}

afterEach(() => {
  jest.clearAllMocks()
})

 test.only('Given an email and password When executed user login Then the response must have a tokenGiven an email and password When executed user login Then the response must have a token', async () => {
    const name = 'Jolie'
    const email = 'teste@gmail.com'
    const password = '123456'

    const dataToken = {
        data: { token: '123.123.123abcd' },
    }

    const sut = createSut()
    mockHttp.post.mockResolvedValueOnce(dataToken)

    const response = await sut.register(name, email, password)
    expect(mockHttp.post).toHaveBeenCalled()
    expect(response).toHaveProperty('token')
})

UserModel类代码

import { IUser, IUserApiClientUrls, IUserLogged, IUserModelApi, IUserApiRegister } from '@/interfaces'
import httpClient from '@/services/index'

export class UserModel implements IUserModelApi {
    constructor(private readonly url: IUserApiClientUrls) {}

    async register(name: string, email: string, password: string): Promise<IUserApiRegister> {
        const params = {
            url: this.url.register,
            payload: {
                name,
                email,
                password,
            },
        }
        return httpClient
            .post(params.url, params.payload)
            .then((response) => response.data)
            .catch((e) => console.error(e))
    }
}

基于axios.create()实现的httpClient代码

import axios, { AxiosInstance } from 'axios'

const API_ENVS = {
    production: '',
    development: '',
    local: 'http://localhost:3000',
}

const httpClient: AxiosInstance = axios.create({
    baseURL: API_ENVS.local,
})

httpClient.interceptors.response.use(
    (response) => response,
    (error) => {
        return error
    }
)

export default httpClient

用户疑问:我也尝试直接Mock axios但未成功,请问我哪里操作出错了?


问题原因与解决方案

问题原因

jest.mock('@/services/index')会自动生成一个空的mock模块,但httpClient是axios.create()创建的AxiosInstance实例,其post等方法并不是jest的mock函数。你通过as jest.Mocked<typeof httpClient>做类型断言只是告诉TypeScript这是一个mock对象,但实际运行时mockHttp.post并没有被替换成可调用mockResolvedValueOnce的jest mock函数,因此报错。

解决方案一:手动Mock httpClient的方法

在jest.mock之后,手动将httpClient的post方法赋值为jest mock函数:

修改测试文件中的mock部分:

jest.mock('@/services/index')
const mockHttp = httpClient as jest.Mocked<typeof httpClient>
// 手动把post设置为jest mock函数
mockHttp.post = jest.fn()

测试用例中的逻辑无需改动,此时mockHttp.post已经是标准的jest mock函数,可以正常调用mockResolvedValueOnce。

解决方案二:Mock axios模块本身

因为httpClient是通过axios.create()创建的,你可以直接mock axios模块,让create方法返回带有mock请求方法的实例:

修改测试文件:

import axios from 'axios'
import { IUserApiClientUrls } from '../../../src/interfaces'
import { UserModel } from '@/models/UserModel'
import httpClient from '@/services/index'
import apiClient from '@/services/server.json'

// Mock axios整个模块
jest.mock('axios')
const mockAxios = axios as jest.Mocked<typeof axios>

const createSut = () => {
  const urlUser: IUserApiClientUrls = apiClient.user
  return new UserModel(urlUser)
}

afterEach(() => {
  jest.clearAllMocks()
})

 test.only('Given an email and password When executed user login Then the response must have a token', async () => {
    const name = 'Jolie'
    const email = 'teste@gmail.com'
    const password = '123456'

    const dataToken = {
        data: { token: '123.123.123abcd' },
    }

    // 让axios.create返回带有mock post方法的实例
    mockAxios.create.mockReturnValue({
      post: jest.fn().mockResolvedValueOnce(dataToken),
      interceptors: {
        response: { use: jest.fn() }
      }
    } as any)

    const sut = createSut()
    const response = await sut.register(name, email, password)
    
    expect(mockAxios.create).toHaveBeenCalled()
    expect(mockAxios.create().post).toHaveBeenCalledWith(apiClient.user.register, { name, email, password })
    expect(response).toHaveProperty('token')
})

额外注意点

UserModel的register方法中,catch块只做了console.error(e),没有抛出错误,这样如果请求失败,测试无法捕获到异常。建议修改为:

.catch((e) => {
  console.error(e)
  throw e // 抛出错误,让调用方可以处理
})

内容的提问来源于stack exchange,提问作者Merieli Manzano

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 00:45:27