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

使用Chai-HTTP测试API时传URL返回426,传Server实例正常

排查chai-HTTP传入URL返回426状态码的问题

使用Chai结合chai-HTTP测试API时,传入Express Server实例的测试可正常通过,但传入URL(http://localhost:3000)时第二个测试返回426状态码。该接口在Postman中可正常调用,需排查原因。

测试代码

const { expect } = require('chai')
const chai = require('chai')
const chaiHttp = require('chai-http');

chai.use(chaiHttp);

const server = require('../index')

const methodUrlBase = '/api/platforms'

describe('platforms', () => {
  it('Should return platforms - works', async () => {
    const res = await chai.request(server)
      .get(methodUrlBase)
    expect(res.status).to.equal(200)
  })

  it('Should return platforms - doesn\'t work', async () => {
    const res = await chai.request('http://localhost:3000')
      .get(methodUrlBase)
    expect(res.status).to.equal(200)
  })
})

服务端代码

const express = require('express');

const app = express();

app.use(express.json());

app.get('/api/platforms', (req, res) => {
    return res.send({ resp: 'hardcoded' });
});

const server = app.listen(3000, () => console.log('Server up'));

module.exports = server

原因分析

问题核心在于chai-HTTP对传入Server实例的请求会自动管理服务器生命周期:当第一个测试使用chai.request(server)时,测试完成后chai-HTTP会调用server.close()关闭服务器。此时第二个测试用URL发起请求时,服务器已经关闭,若端口被其他服务占用或处于TIME_WAIT状态,就会返回异常的426状态码(而非预期的连接失败)。而Postman能正常访问是因为测试运行前服务器已启动,且Postman请求不会触发chai-HTTP的自动关闭逻辑。

解决方法

方法1:手动控制服务器生命周期

在测试套件中统一管理服务器的启动与关闭,避免chai-HTTP自动关闭服务器:

const { expect } = require('chai')
const chai = require('chai')
const chaiHttp = require('chai-http');

chai.use(chaiHttp);

const server = require('../index')

const methodUrlBase = '/api/platforms'

describe('platforms', () => {
  // 所有测试完成后手动关闭服务器
  after((done) => {
    server.close(done);
  })

  it('Should return platforms - works', async () => {
    const res = await chai.request(server)
      .get(methodUrlBase)
    expect(res.status).to.equal(200)
  })

  it('Should return platforms - now works', async () => {
    const res = await chai.request('http://localhost:3000')
      .get(methodUrlBase)
    expect(res.status).to.equal(200)
  })
})

方法2:分离Express App与服务器启动逻辑

修改服务端代码,仅导出Express App实例,由测试或单独的启动文件负责启动服务器,避免提前启动导致的生命周期冲突:

修改后的服务端代码(index.js):

const express = require('express');

const app = express();

app.use(express.json());

app.get('/api/platforms', (req, res) => {
    return res.send({ resp: 'hardcoded' });
});

module.exports = app;

新增启动文件(start.js):

const app = require('./index');
const server = app.listen(3000, () => console.log('Server up'));
module.exports = server;

修改后的测试代码:

const { expect } = require('chai')
const chai = require('chai')
const chaiHttp = require('chai-http');

chai.use(chaiHttp);

const app = require('../index')

const methodUrlBase = '/api/platforms'

describe('platforms', () => {
  it('Should return platforms - works with app instance', async () => {
    const res = await chai.request(app)
      .get(methodUrlBase)
    expect(res.status).to.equal(200)
  })

  it('Should return platforms - works with URL', async () => {
    const server = app.listen(3000);
    try {
      const res = await chai.request('http://localhost:3000')
        .get(methodUrlBase)
      expect(res.status).to.equal(200)
    } finally {
      // 测试完成后关闭服务器
      server.close();
    }
  })
})

内容的提问来源于stack exchange,提问作者Gervasius Twinklewinkleson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 18:42:02