如何测试含嵌套Axios请求的异步服务?Jest测试问题求助
解决思路:Mock内部依赖,专注核心逻辑测试
你的问题核心在于:persons.get里先调用了异步的places.get,但测试中没有处理这个依赖的Promise,导致代码卡在等待places.get响应的阶段,根本没走到personsAgent.getAll的调用步骤。要解决这个问题,我们需要先Mock掉places服务的get方法,让它直接返回预设的模拟数据,这样代码才能继续执行后续逻辑。
下面提供两种可行的方案,都是基于Jest本身的Mock能力,不需要额外工具:
方案一:Mock整个places服务模块
直接用jest.mock替换掉整个places模块,让它的get方法返回我们需要的模拟响应:
import mockAxios from 'jest-mock-axios'; import persons from './persons'; import places from './places'; // 全局Mock places服务,让get方法默认返回空的地点数据 jest.mock('./places', () => ({ get: jest.fn().mockResolvedValue({ data: { results: [] }, status: 200 }) })); afterEach(() => { mockAxios.reset(); }); it('returns Persons data from API with mapped place info', async () => { // 1. 自定义本次测试的places响应数据(匹配后续person的过滤逻辑) const mockPlacesData = { data: { results: [{ id: 123, name: 'New York' }] }, status: 200 }; places.get.mockResolvedValueOnce(mockPlacesData); // 2. 调用待测试的persons.get方法 const requestPromise = persons.get({ search: '', limit: 10, offset: 0 }); // 3. 断言places.get被正确调用 expect(places.get).toHaveBeenCalledWith({ search: '', limit: 1000, offset: 0 }); // 4. 断言personsAgent.getAll对应的axios请求被触发 expect(mockAxios.get).toHaveBeenCalledWith('/persons/', { params: { search: null, limit: 10, offset: 0 }, }); // 5. 模拟axios返回persons数据(注意要包含匹配place_id的字段) const mockPersonsResponse = { data: { results: [{ id: 456, first_name: 'Alice', last_name: 'Smith', place_id: 123 // 和上面mock的place id对应 }] }, status: 200 }; mockAxios.mockResponse(mockPersonsResponse); // 6. 等待Promise完成,断言最终返回结果 const finalResult = await requestPromise; expect(finalResult).toEqual({ data: { ...mockPersonsResponse.data, results: [{ id: 456, name: 'Alice', surname: 'Smith', place: mockPlacesData.data.results[0] }] }, status: 200 }); });
方案二:用jest.spyOn局部Mockplaces.get方法
如果你不想替换整个places模块,只想Mock它的get方法,可以用jest.spyOn来实现局部Mock:
import mockAxios from 'jest-mock-axios'; import persons from './persons'; import places from './places'; afterEach(() => { mockAxios.reset(); jest.restoreAllMocks(); // 每次测试后恢复被Spy的方法 }); it('returns Persons data from API', async () => { // 1. Spy并Mock places.get方法 const placesGetSpy = jest.spyOn(places, 'get').mockResolvedValue({ data: { results: [{ id: 789, name: 'London' }] }, status: 200 }); // 2. 调用persons.get const requestPromise = persons.get({ search: 'test', limit: 5, offset: 0 }); // 3. 验证places.get的调用参数 expect(placesGetSpy).toHaveBeenCalledWith({ search: '', limit: 1000, offset: 0 }); // 4. 验证persons的axios请求参数(此时search有值,不会是null) expect(mockAxios.get).toHaveBeenCalledWith('/persons/', { params: { search: 'test', limit: 5, offset: 0 }, }); // 5. 模拟axios返回persons数据 const mockPersonsResponse = { data: { results: [{ id: 101, first_name: 'Bob', last_name: 'Brown', place_id: 789 }] }, status: 200 }; mockAxios.mockResponse(mockPersonsResponse); // 6. 断言最终结果 const finalResult = await requestPromise; expect(finalResult.data.results[0].place.name).toBe('London'); expect(finalResult.status).toBe(200); });
关键说明
为什么之前的测试失败?
因为places.get是异步函数,测试中没有处理它的Promise,代码会一直等待它的响应,永远走不到personsAgent.getAll的调用步骤,所以你只能看到/places/的axios请求,看不到/persons/的。Mock内部依赖的优势
我们不需要关心places.get的真实实现,只需要预设它的返回值,就能专注测试persons.get的核心逻辑:参数处理、数据排序映射、axios调用等。异步测试的最佳实践
尽量使用await等待Promise完成,而不是用then/catch回调,这样代码更清晰,断言也更准确,避免出现回调未执行的问题。
内容的提问来源于stack exchange,提问作者Bartek Ł.
相关产品推荐
相关产品推荐

