You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 20:06:02