GitLab流水线中Cypress测试完成后无法自动退出问题排查
GitLab流水线Cypress测试完成后任务无法自动退出的解决方法
问题场景
使用cypress/included:12.8.1镜像在GitLab流水线中运行Cypress测试,已通过设置entrypoint: [""]解决默认入口点自动执行cypress run找不到配置文件的问题。但测试完成后,流水线任务无法自动退出,最终因1小时超时失败。调试日志显示Cypress已准备以0码退出,但后台运行的Next服务器进程未终止,导致任务无法结束。
当前GitLab CI配置如下:
--- cypress tests: image: name: cypress/included:12.8.1 entrypoint: [""] stage: e2e-tests interruptible: true dependencies: [] cache: key: ${CI_COMMIT_REF_SLUG} paths: - .npm - cache/Cypress rules: - exists: - cypress.config.js artifacts: # Store cypress screenshots as artifacts on job failure only when: on_failure expire_in: 1 day paths: - ${CI_PROJECT_DIR}/cypress/screenshots - ${CI_PROJECT_DIR}/cypress/videos script: - npm ci - npm run build --omit=dev - npm run start & - DEBUG=cypress:server* npx cypress run --headless --config-file cypress.config.js -c video=false
问题原因
通过npm run start &将Next服务器放到后台运行后,测试完成时该进程未被终止。GitLab CI会等待所有子进程结束才会终止任务,因此导致超时。
解决方案
方案1:记录服务器PID并在测试后终止进程
修改脚本,启动服务器时记录其PID,Cypress测试完成后手动杀死该进程:
script: - npm ci - npm run build --omit=dev # 启动服务器并记录PID到文件 - npm run start & echo $! > next.pid # 等待服务器启动完成(可根据实际情况调整等待时间) - sleep 30 - DEBUG=cypress:server* npx cypress run --headless --config-file cypress.config.js -c video=false # 杀死后台的Next服务器进程,添加|| true避免进程不存在时报错 - kill $(cat next.pid) || true
方案2:使用wait-on等待服务器就绪,精准终止进程
先安装wait-on工具,等待服务器指定端口就绪后再运行测试,测试结束后杀死Next相关进程:
script: - npm ci - npm install -g wait-on - npm run build --omit=dev - npm run start & # 等待Next默认3000端口就绪 - wait-on http://localhost:3000 - DEBUG=cypress:server* npx cypress run --headless --config-file cypress.config.js -c video=false # 杀死所有与next start相关的进程 - pkill -f "next start" || true
方案3:让Cypress自动管理服务器生命周期
修改cypress.config.js,通过setupNodeEvents配置Cypress在测试前后启动和关闭服务器,无需在CI脚本中手动处理:
// cypress.config.js const { defineConfig } = require('cypress') const next = require('next') module.exports = defineConfig({ e2e: { setupNodeEvents(on, config) { on('before:run', async () => { // 启动生产模式的Next服务(已执行build) const app = next({ dev: false }) await app.prepare() const handle = app.getRequestHandler() config.server = require('http').createServer((req, res) => { handle(req, res) }) config.server.listen(3000) return config }) on('after:run', (config) => { // 测试完成后关闭服务器 config.server.close() }) }, baseUrl: 'http://localhost:3000' } })
对应CI脚本可简化为:
script: - npm ci - npm run build --omit=dev - DEBUG=cypress:server* npx cypress run --headless --config-file cypress.config.js -c video=false
推荐选择
优先选择方案1或方案2,配置简单且无需修改Cypress核心配置;若希望更优雅地管理服务器生命周期,可使用方案3。
内容的提问来源于stack exchange,提问作者mdailey77
相关产品推荐
相关产品推荐

