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

本地双Express服务跨端口调用:chai-http测试时Axios请求被拒绝

解决测试Express服务A时调用服务B出现ECONNREFUSED的问题

你遇到的connect ECONNREFUSED 127.0.0.1:3020错误,本质是测试运行时服务B根本没启动(或者端口配置不匹配)——毕竟Postman能正常访问服务B,说明服务B本身的逻辑和接口是没问题的。我帮你拆解几个最可能的原因和解决办法:

1. 先查服务B的启动逻辑(最可能的坑)

看你服务B的listen函数里有一行注释掉的代码:if (app.get('env') === 'test') return;,如果你的测试环境把NODE_ENV设成了test,哪怕你没打开这行注释,也有可能测试框架或配置让服务B跳过了端口监听。

解决办法:

  • 直接去掉这个环境判断,或者修改逻辑,让测试环境下服务B也能启动监听端口;
  • 或者在测试脚本里显式启动服务B的实例,而不是依赖单独运行的服务。比如在测试文件的前置钩子中启动服务B:
    const appB = require('../path/to/serviceB/app');
    let serverB;
    
    // 测试前启动服务B
    before((done) => {
      serverB = appB.listen(3020, () => {
        console.log('测试用服务B已启动在3020端口');
        done();
      });
    });
    
    // 测试结束后关闭服务B
    after((done) => {
      serverB.close(done);
    });
    

2. 检查测试环境的变量配置

服务A里用process.env.userapi拼接请求地址,测试时这个环境变量可能没正确加载:

  • 要么是测试环境没加载对应的.env文件(比如你用了dotenv但没指定测试环境的配置文件);
  • 要么是userapi的值和服务B的测试端口不匹配。

解决办法:

  • 在测试文件开头加载测试专用的环境变量:
    require('dotenv').config({ path: '.env.test' });
    
  • 或者手动在测试代码里指定变量:
    process.env.userapi = 'http://localhost:3020';
    

3. 测试执行顺序的时序问题

有时候测试代码跑得太快,服务B还没完全启动就绪,服务A就发起了请求,导致连接被拒绝。

解决办法:

  • 用测试框架的before/beforeEach钩子确保服务B完全启动后再执行测试(上面的例子已经包含了这种处理);
  • 如果是异步启动,可以用Promise或者回调函数等待服务启动完成的信号。

4. 跨域配置的小优化(非直接原因,但可以统一)

你服务B的跨域配置里重复设置了Access-Control-Allow-Origin,虽然这不是导致连接拒绝的原因,但可以统一成一次设置,避免冗余:

res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', true);
res.setHeader( 'Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, appid' );
res.setHeader( 'Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS' );
if (req.method == 'OPTIONS') { 
  res.status(200).end(); 
} else { 
  next(); 
}

完整测试示例(Mocha + Chai-HTTP)

给你一个完整的测试流程参考,确保服务A和服务B在测试中协同运行:

const chai = require('chai');
const chaiHttp = require('chai-http');
const appA = require('../serviceA/app');
const appB = require('../serviceB/app');
require('dotenv').config({ path: '.env.test' });

chai.use(chaiHttp);
const expect = chai.expect;

let serverB;

before((done) => {
  // 启动服务B
  serverB = appB.listen(3020, done);
});

after((done) => {
  // 关闭服务B
  serverB.close(done);
});

describe('服务A - 调用服务B邀请接口', () => {
  it('应该成功调用服务B的/invite接口', (done) => {
    chai.request(appA)
      .post('/service-a-invite-endpoint') // 服务A中触发调用B的接口
      .send({ email: 'test@example.com' })
      .end((err, res) => {
        expect(err).to.be.null;
        expect(res).to.have.status(200);
        // 可以根据实际需求添加更多断言
        done();
      });
  });
});

核心思路就是:测试环境中必须确保服务B处于运行状态,并且服务A能正确访问到它的地址,优先检查服务B的启动逻辑和环境变量配置,这两个是最容易踩的坑。

内容的提问来源于stack exchange,提问作者Guillaume Badi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 07:48:16