
本文详解 Cypress 端到端测试中本地服务器(Express)的正确启动方式,解决因错误地在浏览器环境 require Node.js 服务代码导致的 Webpack 编译失败(如 Unexpected character '#')及服务不可用问题。
本文详解 cypress 端到端测试中本地服务器(express)的正确启动方式,解决因错误地在浏览器环境 require node.js 服务代码导致的 webpack 编译失败(如 unexpected character '#')及服务不可用问题。
在 Cypress 测试中直接通过 require('./index.js') 启动 Express 服务器是一种常见误区——Cypress 的测试文件(.spec.js)运行在浏览器环境(Electron/Chromium)下,而 Express、fs、mysql2 等 Node.js 原生模块仅能在 Node.js 进程中执行。当你在 test.spec.js 中 require 服务入口文件时,Webpack 尝试将服务端代码打包进浏览器 bundle,从而触发编译错误(例如 Module parse failed: Unexpected character '#'),这正是 lru-cache 等现代 Node 模块使用私有字段(#constructing)导致解析失败的典型表现。
✅ 正确做法:分离运行时环境——让 Express 服务在独立的 Node.js 进程中运行,Cypress 测试进程仅作为 HTTP 客户端发起请求。
✅ 推荐方案:使用 start-server-and-test 工具
该工具可自动化完成「启动服务 → 等待服务就绪 → 运行 Cypress → 服务自动关闭」全流程,安全可靠,且完全规避浏览器端加载服务代码的风险。
1. 安装依赖
npm install --save-dev start-server-and-test
2. 修正你的 index.js(服务入口)
⚠️ 当前代码存在两个关键问题:
-
module.exports = test();立即执行并返回undefined(app.listen()返回值非函数); - 缺少服务关闭能力(影响 CI/CD 清理)。
优化后的 index.js(支持优雅启停):
const express = require('express');
const app = express();
const port = 3000;
const programmingLanguagesRouter = require('./routes/programmingLanguages');
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.get('/', (req, res) => {
res.json({ message: 'ok' });
});
app.use('/programming-languages', programmingLanguagesRouter);
// 错误处理中间件(保持不变)
app.use((err, req, res, next) => {
const statusCode = err.statusCode || 500;
console.error(err.message, err.stack);
res.status(statusCode).json({ message: err.message });
});
// 导出启动和关闭函数(供 CLI 或测试工具调用)
function start() {
return app.listen(port, () => {
console.log(`✅ Server listening at http://localhost:${port}`);
});
}
function close(server) {
if (server) server.close(() => console.log('? Server closed'));
}
module.exports = { start, close };
3. 配置 package.json 脚本
{
"scripts": {
"server:start": "node index.js",
"cy:open": "cypress open",
"cy:run": "cypress run",
"test:e2e": "start-server-and-test server:start http://localhost:3000 cy:open",
"test:e2e:ci": "start-server-and-test server:start http://localhost:3000 cy:run"
}
}
?
start-server-and-test默认会轮询http://localhost:3000直到返回 HTTP 200(即你的/路由响应成功),再启动 Cypress。你也可通过--healthcheck指定自定义健康检查路径(如/health)。
4. 简化你的测试文件(test.spec.js)
无需任何 require,直接发起请求即可:
describe('Calling the endpoint', () => {
it('should return 200 for /programming-languages', () => {
cy.request({
method: 'GET',
url: 'http://localhost:3000/programming-languages',
headers: {
accept: 'application/json'
}
}).then((res) => {
expect(res.status).to.eq(200);
// 可继续断言响应体结构
expect(res.body).to.be.an('array');
});
});
});
⚠️ 注意事项
-
禁止在
.spec.js中require服务代码或任何 Node.js 模块(如express,mysql2,fs); - 确保
cypress.config.js中未启用experimentalSourceRewriting: true(该选项可能干扰服务模块解析); - 在 CI 环境中,建议使用
test:e2e:ci脚本,并添加超时配置(如--timeout 120); - 若需在测试中动态控制服务(如模拟数据库故障),应通过 API(如
/__mock/error)在服务内部实现,而非从 Cypress 控制进程。
✅ 总结
Cypress 测试与后端服务必须运行在隔离的进程中。start-server-and-test 是官方推荐的最佳实践,它消除了手动管理进程生命周期的复杂性,避免了 Webpack 打包错误,并确保测试环境稳定可重现。牢记:测试文件只负责“发请求”,不负责“起服务”——这是架构清晰与工程健壮性的基石。










