如何在Cypress首个测试运行前执行前置命令?
需求与解决方案
核心需求
需要在首个测试执行前仅运行一次命令,获取被测应用(本地/Staging/Production)中window.featureFlags的值,保存到fixtures/feature-flags.json中。后续所有测试通过该文件判断功能是否启用,自动跳过未启用功能的测试用例:
beforeEach(function(){ if( !isFeatureEnabled( 'FEATURENAME' ) ){ this.skip(); } });
期望执行流程:
- 运行单个测试文件(如
test-header.cy.ts):先获取并保存feature flags,再执行测试 - 运行多个测试文件:先执行一次feature flags获取与保存,再依次运行所有测试文件
已尝试的方案及问题
方案1:e2e.ts中添加before钩子
在全局配置文件中添加before钩子实现逻辑,代码如下:
// Add isFeaturedEnabled globally (so we don't need to import it every time) function isFeatureEnabled(featureName) { if (!globalThis.featureFlags) { throw new Error('Feature flags are not set'); } const feature = globalThis.featureFlags.find((feature) => feature.name === featureName); return Boolean(feature && feature.enabled); } globalThis.isFeatureEnabled = isFeatureEnabled; // Attach the function to the global scope // END OF: Add isFeaturedEnabled globally before(() => { const filePath = 'cypress/fixtures/tmp/featureFlags.json'; // Reset featureFlags, to ensure to get the latest one cy.task('fileExists', filePath) .then((exists) => { if (exists) { cy.exec(`rm ${filePath}`); } }) .then(() => { cy.visit('/'); cy.window().then((win) => { if (win.featureFlags && win.featureFlags.length > 0) { cy.writeFile(filePath, win.featureFlags); globalThis.featureFlags = win.featureFlags; } else { cy.writeFile(filePath, []); globalThis.featureFlags = []; } }); }); });
问题:每次运行单个测试文件时都会重新访问页面生成文件,重复操作浪费时间;且强制删除已有文件,无复用逻辑。
方案2:前置bash脚本
编写单独脚本在测试前生成fixture文件。
问题:切换环境时容易遗忘执行脚本,导致使用旧的feature flags,引发测试误判。
最优解决方案
利用Cypress全局before钩子的特性(单次测试会话仅执行一次),结合文件复用逻辑实现需求,无需手动操作且避免重复资源消耗。
步骤1:配置全局钩子与工具函数
修改e2e.ts文件,添加全局isFeatureEnabled函数和优化后的before钩子:
// e2e.ts // 全局挂载功能判断函数,所有测试文件可直接调用 function isFeatureEnabled(featureName: string): boolean { if (!globalThis.featureFlags) { throw new Error('Feature flags未初始化,请检查全局钩子执行情况'); } const targetFeature = globalThis.featureFlags.find(f => f.name === featureName); return Boolean(targetFeature?.enabled); } globalThis.isFeatureEnabled = isFeatureEnabled; // 全局before钩子:单次测试会话仅执行一次 before(() => { const fixturePath = 'cypress/fixtures/tmp/featureFlags.json'; // 优先读取已有fixture文件,避免重复访问页面 cy.task('readFileSafely', fixturePath) .then((existingFlags) => { if (existingFlags && Array.isArray(existingFlags)) { globalThis.featureFlags = existingFlags; return; } // 文件不存在或无效时,访问页面获取最新feature flags cy.visit('/'); cy.window().then(win => { const latestFlags = win.featureFlags || []; globalThis.featureFlags = latestFlags; cy.writeFile(fixturePath, latestFlags); }); }); });
步骤2:添加Node端文件读取任务
在cypress.config.ts中添加readFileSafely任务,用于安全读取文件(避免文件不存在时抛出错误):
// cypress.config.ts import { defineConfig } from 'cypress'; import fs from 'fs'; export default defineConfig({ e2e: { setupNodeEvents(on) { on('task', { readFileSafely(filePath: string) { return fs.existsSync(filePath) ? JSON.parse(fs.readFileSync(filePath, 'utf8')) : null; } }); }, }, });
方案优势
- 单次会话仅执行一次获取操作:不管运行多少个测试文件,全局
before钩子在单次cypress run或GUI测试会话中仅执行一次 - 复用已有fixture:若文件存在则直接读取,避免重复访问页面,节省时间
- 自动适配环境:切换环境后重新运行测试时,若fixture文件不存在或无效,会自动获取最新feature flags,无需手动执行脚本
- 全局函数调用便捷:所有测试文件可直接使用
isFeatureEnabled,无需重复导入
测试文件使用示例
在任意测试文件中,直接通过全局函数判断并跳过未启用功能的测试:
// test-header.cy.ts describe('头部导航测试', () => { beforeEach(function() { if (!isFeatureEnabled('HEADER_NEW_STYLE')) { this.skip(); } }); it('新样式头部导航显示正常', () => { // 测试逻辑代码 }); });
内容的提问来源于stack exchange,提问作者Zeth
相关产品推荐
相关产品推荐

