解决多TypeScript API批量测试中的服务器启动异常问题
批量API测试时服务器关闭错误的解决方案
问题描述
测试Restaurant和Employee两个API的POST、GET方法,API本身运行正常。测试文件分别存放在test\restaurant\index.test.ts和test\employee\index.test.ts,单独运行单个测试文件时全部通过,但执行批量测试脚本:
npm run knex:latest && npm run test:compile && npm run test:script
时部分测试失败,报错:
Error [ERR_SERVER_NOT_RUNNING]: Server is not running.
错误原因
- 进程提前终止:每个测试文件的
after钩子中调用了process.exit(0),第一个测试套件执行完毕后直接终止整个Node进程,导致第二个测试套件无法正常启动服务器。 - 无状态检查的服务器关闭:调用
server.close()前未检查服务器是否处于运行状态,触发未运行实例的关闭错误。 - 模块缓存复用:多次导入
../../src/index时,Node.js会复用模块缓存,可能拿到已经关闭的服务器实例。 - 接口路径错误:Employee测试的GET接口路径错误(原代码使用
/api/v1/offerings,实际应为/api/v1/employees)。
修复方案
- 移除所有测试文件
after钩子中的process.exit()调用,让测试框架自动管理测试流程。 - 给服务器关闭逻辑增加状态检查,仅当服务器处于监听状态时才执行关闭操作。
- 在每个测试的
before钩子中清理模块缓存,确保每次导入src/index都能获取全新的服务器实例。 - 修正Employee测试中的GET接口路径错误。
修改后的测试代码
test\restaurant\index.test.ts
import { before, describe, it, after } from 'node:test'; import assert from 'node:assert'; import { promisify } from 'node:util'; import { Server } from 'http'; describe('Restaurant Test Suite', () => { let serverTest: Server; let isServerListening = false; before(async () => { // 清理模块缓存,确保每次获取新的服务器实例 delete require.cache[require.resolve('../../src/index')]; const { server } = await import('../../src/index'); serverTest = server; // 监听服务器启动事件,标记运行状态 serverTest.on('listening', () => { isServerListening = true; }); }); after(async () => { try { if (isServerListening) { await promisify(serverTest.close.bind(serverTest))(); } } catch (err) { console.error('关闭服务器时出错:', err); } }); it('Restaurant Post', async (t) => { const testPort = process.env.PORT; const testServerAddress = `http://localhost:${testPort}/api/v1/restaurant`; await t.test('it should create a restaurant', async () => { const data = { name: 'Test Name', active: false, description: 'Test Description', }; const request = await fetch(testServerAddress, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify(data), }); assert.strictEqual(request.status, 200); interface Result { message: string; } const result = await (<Promise<Result>>request.json()); assert.deepStrictEqual( result.message, 'Restaurant created successfully', '返回正确的创建成功消息', ); }); }); it('Get Restaurants', async (t) => { const testPort = process.env.PORT; const testServerAddress = `http://localhost:${testPort}/api/v1/restaurants`; await t.test('it should get restaurants', async () => { const request = await fetch(testServerAddress, { method: 'GET', }); assert.strictEqual(request.status, 200); interface Data { id: string; } interface Result { data: Array<Data>; } const result = await (<Promise<Result>>request.json()); assert.ok(result.data[0].id.length > 30, '返回的餐厅UUID格式正确'); }); }); });
test\employee\index.test.ts
import { before, describe, it, after } from 'node:test'; import assert from 'node:assert'; import { promisify } from 'node:util'; import { Server } from 'http'; describe('Employee Test Suite', () => { let serverTest: Server; let isServerListening = false; before(async () => { // 清理模块缓存,确保每次获取新的服务器实例 delete require.cache[require.resolve('../../src/index')]; const { server } = await import('../../src/index'); serverTest = server; // 监听服务器启动事件,标记运行状态 serverTest.on('listening', () => { isServerListening = true; }); }); after(async () => { try { if (isServerListening) { await promisify(serverTest.close.bind(serverTest))(); } } catch (err) { console.error('关闭服务器时出错:', err); } }); it('Employee Post', async (t) => { const testPort = process.env.PORT; const testServerAddress = `http://localhost:${testPort}/api/v1/employee`; await t.test('it should create a employee', async () => { const data = { name: 'Test Employee Name', employee_availability: [] }; const request = await fetch(testServerAddress, { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify(data), }); assert.strictEqual(request.status, 200); interface Result { message: string; } const result = await (<Promise<Result>>request.json()); assert.deepStrictEqual( result.message, 'Employee created successfully', '返回正确的创建成功消息', ); }); }); it('Get Employees', async (t) => { const testPort = process.env.PORT; // 修正接口路径错误 const testServerAddress = `http://localhost:${testPort}/api/v1/employees`; await t.test('it should get employees', async () => { const request = await fetch(testServerAddress, { method: 'GET', }); assert.strictEqual(request.status, 200); interface Data { id: string; } interface Result { data: Array<Data>; } const result = await (<Promise<Result>>request.json()); assert.ok(result.data[0].id.length > 30, '返回的员工UUID格式正确'); }); }); });
验证步骤
- 保存修改后的两个测试文件。
- 执行批量测试脚本:
npm run knex:latest && npm run test:compile && npm run test:script
- 确认所有测试用例均通过,无
ERR_SERVER_NOT_RUNNING错误。
内容的提问来源于stack exchange,提问作者ENV
相关产品推荐
相关产品推荐

