使用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
相关产品推荐
相关产品推荐

