CircleCI运行Cypress测试无法找到下载的Excel文件解决方案
问题结论
CircleCI 完全支持文件下载类自动化测试场景,返回「File not found」是路径配置、运行环境差异、逻辑兼容问题导致,并非平台不支持该类场景。
根因分析
- 硬编码相对路径适配性差:代码中写死
../CreateAutomatedTests/cypress/downloads/路径,本地执行的工作目录层级和CircleCI容器内的工作目录层级不一致,拼接后的路径在CI环境下不指向真实存储位置。 - 下载逻辑存在浏览器兼容限制:代码中注释已标注,点击后重载页面防无限加载的逻辑仅在Chrome下生效,若CircleCI任务默认用Electron等其他浏览器运行,该逻辑不会触发,文件下载流程会被中断。
- 固定等待逻辑不可靠:使用
cy.wait(4000)、cy.wait(10000)固定时长等待下载完成,CI环境的网络、机器性能普遍弱于本地,固定时长无法覆盖慢网速场景,会出现校验时文件还未下载完成的情况;且点击后5秒强制重载页面的逻辑,在CI环境下极易直接打断未完成的下载流程。 - 未显式指定下载目录:Cypress在CI环境下如果没有显式配置downloads路径,可能会将文件存到临时目录,不会落到项目内的预期路径下。
- 文件名处理规则不兼容:当前仅做了冒号替换、空格移除处理,不同操作系统、浏览器对下载文件名的转义规则存在差异,CI环境为Linux系统,和本地Windows/macOS的文件名处理逻辑可能不一致,导致拼接的文件名和实际下载文件名不匹配。
修复方案
- 显式固定Cypress下载目录,移除硬编码路径
在Cypress配置文件(cypress.config.js/旧版本为cypress.json)中显式指定下载目录,统一本地和CI环境的存储路径:
const { defineConfig } = require("cypress"); module.exports = defineConfig({ downloadsFolder: "cypress/downloads", // 固定为项目根目录下的相对路径,不要添加上层目录前缀 // 保留原有其他配置 })
测试代码中删除手动拼接的../CreateAutomatedTests/cypress/downloads/路径前缀,cy-verify-downloads插件默认会从配置好的downloadsFolder目录下查找文件,不需要手动拼全路径。
- 统一CI和本地的运行浏览器
在CircleCI配置文件.circleci/config.yml中,指定Cypress用Chrome运行,和本地环境保持一致,避免浏览器行为差异:
- run: name: 执行Cypress测试 command: npx cypress run --browser chrome
使用Cypress官方提供的CircleCI镜像即可直接使用Chrome,无需额外安装。
替换不可靠的等待逻辑
删除代码中监听click事件后5秒重载页面的逻辑,cy-verify-downloads本身自带轮询检查机制,会持续判断文件是否下载完成,不需要手动加固定等待、手动重载页面,避免打断下载流程。增加文件名校验逻辑
在执行下载校验前,先打印下载目录下的所有文件,确认CI环境下实际生成的文件名,调整文件名处理规则:
首先在plugins/index.js中新增读取目录的任务:
const fs = require('fs') module.exports = (on, config) => { // 保留原有的checkExcelSheetContents等任务 on('task', { readdir(folderPath) { return fs.readdirSync(folderPath) } }) }
然后在测试代码中调用该任务,打印实际文件列表:
// 下载动作执行后,校验文件前添加 cy.task('readdir', Cypress.config('downloadsFolder')).then(files => { cy.log('下载目录文件列表:', files) })
- 配置CI制品留存方便排查
在CircleCI配置中,将下载目录存为构建制品,若后续仍有问题可以直接下载查看目录内的实际文件:
- store_artifacts: path: cypress/downloads destination: 下载文件留存
现象与代码参考
运行现象
- 本地运行:所有用例正常通过,可正确读取Excel工作表名称完成校验
- CircleCI运行:执行到文件校验步骤时,返回预设的文件缺失错误,提示找不到目标Excel文件
test.js 核心逻辑
cy.get('@transferTestUrl').then((finalLink) => { cy.get('@documentPassword').then((password) => { const linkBody = { username: testdata.credentials.adminAccountInternalTest, password: password, transferTestLink: finalLink, } cy.origin('transfer-develop.test.net',{ args: linkBody }, ({ username, password,transferTestLink })=>{ cy.visit(transferTestLink,{ auth:{ username: username, password: password } }) // 该重载逻辑仅在Chrome下生效,用于防止Cypress下载时无限加载 cy.window().document().then(function (doc) { doc.addEventListener('click', () => { setTimeout(function () { doc.location.reload() }, 5000) }) cy.get('#downloadButton').click({force:true}) cy.wait(4000) }) }) }) }) cy.get('span').contains('courses').invoke('text').then((title) => { cy.wrap(title).as('initialDocumentTitle') cy.get('@initialDocumentTitle').then((initialTitle) => { var convertedTitle = initialTitle.replaceAll(':', "_") cy.wrap(convertedTitle).as('downloadedDocumentTitle') cy.wait(10000) }) }) cy.get('@courseNameArray').then((courseNamesArray) => { cy.log(courseNamesArray) cy.get('@downloadedDocumentTitle').then((documentTitle) => { //读取Excel文件逻辑 var removeSpace = documentTitle.replace(/\s/g, '') var excelFilePath = "../CreateAutomatedTests/cypress/downloads/"+removeSpace cy.verifyDownload(removeSpace, { interval: 600 }) cy.wrap(excelFilePath).as('filePath') cy.get('@filePath').then((filePath) => { cy.task('checkExcelSheetContents', { filePath }).then((contents) => { cy.log(contents) expect(contents).to.deep.equal(courseNamesArray) }) }) }) })
commands.js 配置
require('cy-verify-downloads').addCustomCommand();
内容的提问来源于stack exchange,提问作者Faith Berroya
相关产品推荐
相关产品推荐

