一、Cypress跨域测试的核心难点拆解

1.1 原生同域限制的底层逻辑

举个生活化的例子,你住小区A,要找小区B的物业拿快递,小区B的门禁默认不让你进,因为你不是本小区住户——这就是浏览器的同源策略,简单来说就是:只有当前请求的协议、域名、端口三者完全一致,才会被允许,跨任何一项都会触发跨域限制。平时我们浏览网页时感觉不到,因为大部分请求都是同域的,但做自动化测试时,比如要测电商前端(shop.xxx.com)调用支付接口(pay.xxx.com)的流程,就会直接被浏览器拦掉,这是Cypress跨域测试的第一个基础难点。

1.2 Cypress原生配置的隐形坑

Cypress默认开启了chromeWebSecurity这个安全配置,它会强制让测试环境遵守Chrome的同源策略,哪怕是本地测试跨域接口也会报错,比如常见的No 'Access-Control-Allow-Origin' header或Failed to fetch。除此之外,Cypress的cy.visit默认只能访问配置的baseUrl下的页面,直接访问跨域域名的页面也会触发错误,这些隐形配置坑让很多新手第一次写跨域测试时就卡壳。

二、Cypress跨域测试的可行配置方案

2.1 方案一:关闭Chrome安全模式(临时调试用)

这是最简单的临时方案,只需要修改Cypress的配置文件,把Chrome的安全策略关掉,相当于给测试环境开了“门禁临时放行”,适合本地快速调试接口是否可用。技术栈统一用Cypress+JavaScript,配置示例如下:

// cypress.config.js 核心配置,技术栈:Cypress + JavaScript
const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    baseUrl: 'https://你的前端域名.com', // 替换为自己的前端项目地址
    chromeWebSecurity: false, // 核心:关闭Chrome同源安全策略,允许跨域
    setupNodeEvents(on, config) {
      // 无需额外配置,留空即可满足临时跨域测试需求
      return config
    },
  },
})

这个方案的优点是配置极简单,一行代码搞定;缺点也很明显:生产环境绝对不能用(关闭安全模式会把整个浏览器的安全机制废掉,等于给黑客开了后门),而且只对Chromium内核(Chrome、Edge等)生效,Firefox等其他浏览器不支持。

2.2 方案二:代理转发实现同域模拟(本地联调用)

如果需要调用真实的后端接口(比如和后端联调订单支付),关闭Chrome安全模式就没法测真实接口的跨域头,这时候用代理转发是更合适的方案:把Cypress的请求转发到目标跨域接口,让请求的域名和前端同域,不会被拦。技术栈用Node.js+http-proxy-middleware,示例如下:

// cypress/proxy.js 代理配置文件,技术栈:Node.js + http-proxy-middleware
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = (on, config) => {
  // 给Chrome启动参数加代理,让/api开头的请求走本地代理
  on('before:browser:launch', (browser = {}, launchOptions) => {
    if (browser.family === 'chromium' && browser.name !== 'electron') {
      launchOptions.args.push('--proxy-bypass-list=<-loopback>');
      launchOptions.args.push('--proxy-server=http://localhost:3001'); // 本地代理端口
    }
    return launchOptions;
  });

  // 启动本地代理服务,把/api的请求转发到真实跨域接口
  const express = require('express');
  const app = express();
  // 代理规则:把前端的/api请求转发到https://api.b.com
  const proxy = createProxyMiddleware({
    target: 'https://api.b.com', // 替换为自己的真实跨域接口地址
    changeOrigin: true, // 必须设置:修改请求头的Origin为目标域名,否则会被接口拦截
    pathRewrite: { '^/api': '' }, // 把路径里的/api去掉,比如/api/pay转为https://api.b.com/pay
  });
  app.use('/api', proxy);
  app.listen(3001, () => console.log('Cypress代理已启动:http://localhost:3001'));
};

对应的测试文件示例:

// cypress/e2e/pay-integration.spec.js,技术栈:Cypress + JavaScript
describe('订单支付集成测试', () => {
  it('提交订单后调用真实支付接口成功', () => {
    // 访问前端页面(同域,不会触发跨域)
    cy.visit('/order');
    // 点击提交订单,请求会被代理转发到真实支付接口
    cy.get('#submit-order').click();
    // 验证支付成功的弹窗出现
    cy.get('.pay-success').should('be.visible');
    // 验证接口返回状态正常
    cy.request('/pay/success').its('status').should('eq', 200);
  });
});

这个方案的优点是能调用真实接口,测试结果贴合生产;缺点是需要额外启动Node代理服务,生产环境没法部署代理,适合本地联调用。

2.3 方案三:用intercept Mock跨域请求(生产环境友好)

生产环境既不能改配置,也不能搭代理,这时候用Cypress的intercept功能Mock跨域接口是最优解:不用改任何环境配置,直接拦截跨域请求,返回自定义的模拟数据,适合生产环境的自动化回归测试、异常场景测试。技术栈还是Cypress+JavaScript,示例如下:

// cypress/e2e/pay-prod.spec.js,技术栈:Cypress + JavaScript
describe('生产环境支付流程测试', () => {
  it('模拟支付接口成功的正向场景', () => {
    // 访问生产环境的前端地址(跨域)
    cy.visit('https://prod.shop.xxx.com/order');
    // 核心:拦截支付接口的POST请求,返回自定义成功数据
    cy.intercept('POST', 'https://api.pay.xxx.com/pay', {
      statusCode: 200,
      body: { code: 0, msg: '支付成功', data: { orderId: '20240520001' } },
    }).as('payApi'); // 给拦截器起别名,方便后续等待

    // 触发提交订单,请求被拦截,不会真的调用生产接口
    cy.get('#submit-order').click();
    // 等待拦截的请求完成,验证状态正确
    cy.wait('@payApi').its('response.statusCode').should('eq', 200);
    // 验证前端UI显示正确
    cy.get('.success-tip').should('contain', '订单20240520001支付成功');
  });

  it('模拟支付接口失败的异常场景', () => {
    cy.visit('https://prod.shop.xxx.com/order');
    // 拦截失败的支付请求,返回500错误
    cy.intercept('POST', 'https://api.pay.xxx.com/pay', {
      statusCode: 500,
      body: { code: 500, msg: '支付系统繁忙,请稍后重试' },
    }).as('payFail');

    cy.get('#submit-order').click();
    cy.wait('@payFail');
    // 验证前端正确显示错误提示
    cy.get('.error-tip').should('be.visible').and('contain', '支付系统繁忙');
  });
});

这个方案的优点是无环境依赖,本地、预发、生产都能用,还能模拟各种异常场景;缺点是只能测前端逻辑,不能测真实接口的网络异常(比如超时、DNS解析失败),但生产环境的跨域测试90%以上都是测前端流程,所以这个方案最实用。

三、各方案的应用场景、技术优缺点与注意事项

3.1 应用场景

  • 方案一(关闭Chrome安全):仅用于本地快速调试接口,比如刚写完接口,想快速测前端能不能调通,不需要太严谨的测试;
  • 方案二(代理转发):用于前端和后端联调阶段,需要调用真实的后端接口,验证数据交互是否正常;
  • 方案三(intercept Mock):用于生产环境的自动化回归测试、异常场景测试,这是生产环境跨域测试的首选方案。

3.2 技术优缺点对比

  • 方案一:优点是配置最简单,10秒搞定;缺点是安全风险高,不能用于任何正式测试,只适合临时调试;
  • 方案二:优点是测试结果最贴合生产,能验证真实接口的跨域头、参数是否正确;缺点是需要额外的代理服务,生产环境没法用,本地启动麻烦;
  • 方案三:优点是完全无环境依赖,支持异常场景模拟,安全可靠;缺点是不能测真实接口的网络问题,适合测前端逻辑流程。

3.3 实操中的注意事项

  • 方案一:绝对不能把chromeWebSecurity: false提交到代码仓库,测试完成后要立刻改回true;换Firefox等非Chromium浏览器测试时,这个配置无效,要注意浏览器的兼容性;
  • 方案二:代理的pathRewrite要和真实接口路径匹配,比如前端请求的是/api/pay,真实接口路径是/pay,那pathRewrite要写成{ '^/api': '' },否则会出现404;changeOrigin必须设为true,否则目标接口会拦截请求;
  • 方案三:拦截的接口路径要和真实的跨域接口完全一致(包括请求方法、域名、路径),否则拦截不到;如果接口需要认证(比如token),拦截器会自动保留原请求头的认证信息,不需要额外配置,除非需要自定义认证信息。

四、生产环境跨域测试的落地案例

以某电商平台的生产环境支付流程测试为例,该平台前端是https://shop.xxx.com,支付接口是https://api.pay.xxx.com,跨域且不能修改生产环境配置。落地步骤如下:

  1. 安装Cypress:执行npm install cypress --save-dev;
  2. 初始化Cypress:执行npx cypress open,自动生成配置文件;
  3. 复制方案三的测试代码到cypress/e2e/pay-prod.spec.js;
  4. 运行测试:在Cypress界面选择刚才的测试文件,点击运行,即可自动拦截支付接口的请求,模拟成功和失败场景,验证前端流程是否正常。 这个案例完全不需要修改生产环境的任何配置,也不需要搭额外的服务,10分钟就能搞定生产环境的跨域测试,非常适合中小团队的自动化测试。