Azure DevOps运行Vue Jest单元测试时反复出现JSDOM ECONNREFUSED错误求助
首先咱们拆解下这个问题:你本地跑yarn test:unit完全正常,但到Azure DevOps的CI环境里就反复蹦出connect ECONNREFUSED 127.0.0.1:80的错误——虽说不影响测试结果,但看着确实闹心对吧?
问题根源
这个错误的核心是:你的单元测试在CI环境里发起了真实的网络请求,但Azure DevOps的代理机器上并没有运行对应的后端服务。
看你的axios.js配置:Jest默认会把测试环境的process.env.NODE_ENV设为test,而你的代码里没有专门处理这个分支,会走到default逻辑用baseURL = 'webapi/api/v1'。Jest用的jsdom环境中,window.location.origin默认是http://localhost,拼接后就变成了http://localhost/webapi/api/v1,尝试访问本地80端口——但Azure DevOps的CI机器上根本没跑这个服务,自然就被拒绝了。
而本地没问题,要么是你同时开着后端服务,要么是本地测试时无意间Mock了API请求。
解决办法
核心思路是让单元测试完全隔离外部依赖,不发起真实的API请求,咱们从几个层面来处理:
1. 全局Mock axios(最彻底的方式)
给axios做全局Mock,让所有测试里的API调用都走模拟逻辑,不会真发请求:
- 在项目根目录创建
__mocks__/axios.js文件:const mockAxios = jest.createMockFromModule('axios'); // 模拟axios.create方法 mockAxios.create = jest.fn(() => mockAxios); // 模拟你导出的http实例 mockAxios.http = { get: jest.fn().mockResolvedValue({ data: {} }), post: jest.fn().mockResolvedValue({ data: {} }), put: jest.fn().mockResolvedValue({ data: {} }), delete: jest.fn().mockResolvedValue({ data: {} }) }; export default mockAxios; - 然后在
jest.config.js里添加全局Mock配置:module.exports = { // ...你的原有配置 moduleNameMapper: { '^axios$': '<rootDir>/__mocks__/axios.js' } };
2. 给axios添加测试环境分支
在axios.js里明确处理test环境,避免使用真实地址:
let baseURL = ''; switch (process.env.NODE_ENV) { case 'development': { baseURL = 'https://localhost:44359/api/v1'; break; } case 'production': { baseURL = 'webapi/api/v1'; break; } case 'test': { // 新增测试环境配置 baseURL = 'http://mock-api'; // 随便填一个非真实地址,配合Mock使用 break; } case 'docker': { const port = process.env.VUE_APP_WebAPI_PORT; const path = process.env.VUE_APP_WebAPI_Path; const version = process.env.VUE_APP_WebAPI_Version; const url = new URL(window.location.origin); url.port = port; baseURL = `${url.origin.toString()}/${path}/${version}`; break; } default: baseURL = 'webapi/api/v1'; }
3. 调整Jest的jsdom资源配置
你的jest.config.js里设置了resources: 'usable',这会让jsdom尝试加载外部资源(包括发起XHR请求)。改成禁止加载外部资源,从根源上阻止这类请求:
testEnvironmentOptions: { resources: 'none', // 禁止加载任何外部资源 runScripts: 'dangerously' // 如果测试需要运行组件内脚本就保留,不需要可以删除 }
4. 针对单个测试文件自定义Mock
如果某些测试需要特定的返回值,可以在单个文件里单独Mock:
import { http } from '@/axios'; // Mock你的axios实例 jest.mock('@/axios'); describe('你的组件名', () => { it('测试接口请求逻辑', async () => { // 自定义Mock返回值 http.get.mockResolvedValue({ data: { list: [1,2,3] } }); // 执行测试逻辑 // ... // 验证API调用是否符合预期 expect(http.get).toHaveBeenCalledWith('/your-api-path'); }); });
总结
这类问题本质是单元测试没做好依赖隔离——单元测试应该只测组件自身逻辑,而不是真去调用后端API。通过上面的Mock配置和环境调整,就能彻底解决Azure DevOps里的连接拒绝错误了。
内容的提问来源于stack exchange,提问作者Léo Coco

