Cypress面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

服务端阅读 05月29日 01:09

如何配置 Cypress 测试报告和 CI/CD 集成?

Cypress 测试报告配置分两步:选 reporter、配参数。最常用的是 Mochawesome,在 cypress.config.js 中设 reporter 为 'mochawesome',通过 reporterOptions 指定 reportDir、overwrite: false、html: true、chart: true。如需合并多个 spec 的报告,搭配 mochawesome-merge 工具合并 JSON 再生成单份 HTML。CI/CD 集成的关键是:用 npx cypress run --reporter mochawesome 在无头模式执行;通过 --parallel 参数配合 Cypress Cloud 实现并行测试加速;用 actions/upload-artifact 收集报告和失败时的截图/视频;在 workflow 触发条件中绑定 push/pull_request 事件。失败截图和视频默认保存在 cypress/screenshots 和 cypress/videos 目录,CI 中应作为 artifact 上传以便排查。追问mochawesome-merge 的作用是什么?为什么多个 spec 会生成多份报告?Cypress 的 --parallel 参数如何工作?不使用 Cypress Cloud 能实现并行吗?如何在 CI 中只在测试失败时才上传视频和截图?Allure 报告和 Mochawesome 相比各有什么优劣?什么场景该选 Allure?如何在 GitHub Actions 中设置定时跑 Cypress 测试(cron 触发)?写段代码# .github/workflows/cypress.ymlname: Cypresson: [push]jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npx cypress run --reporter mochawesome - uses: actions/upload-artifact@v4 if: always() with: name: report path: cypress/results
服务端阅读 05月29日 00:52

如何在 Cypress 中用 cy.request() 测试 API 接口?

cy.request() 是 Cypress 内置的 HTTP 请求方法,直接在网络层发送请求,无需经过浏览器渲染,适合独立验证后端逻辑。典型场景:绕过 UI 直接测试 API 返回值和状态码、在 UI 测试前通过 API 预置测试数据(如登录获取 token)、验证认证头和权限控制。最佳实践包括:用 Cypress.env() 管理环境变量避免硬编码 URL、用 failOnStatus: false 测试错误响应、结合 cy.intercept() 模拟后端异常、用 response.duration 断言接口性能。注意 cy.request() 默认 4xx/5xx 会导致测试失败,测试异常场景时必须设置 failOnStatusCode: false。追问cy.request() 和 cy.intercept() 有什么区别?什么时候该配合使用?如何用 cy.request() 实现 UI 测试前的数据预置?比通过 UI 操作准备数据有什么优势?测试需要鉴权的 API 时,如何自动获取并注入 Bearer Token?cy.request() 发送的请求会走浏览器的 CORS 限制吗?为什么?如何对 cy.request() 的响应做 Schema 校验而不只是断言个别字段?写段代码// API 测试:登录 + 鉴权 + 错误场景it('returns 401 with wrong password', () => { cy.request({ url: '/api/login', method: 'POST', body: { user: 'admin', pass: 'wrong' }, failOnStatusCode: false }).its('status').should('eq', 401);});
服务端阅读 05月29日 00:52

Cypress 如何处理异步操作?命令链和自动等待机制是什么?

Cypress 的命令不是立即执行,而是入队后按序串行执行。每个命令返回 chainable 对象,后续命令挂载到链条上形成命令队列,Cypress 依次取出执行并自动等待前置条件满足。自动等待指每个命令内建重试机制:cy.get() 会反复查询 DOM 直到元素存在且可见,cy.request() 会等待响应返回,默认超时 4 秒。开发者无需写 sleep 或显式等待,Cypress 在命令间自动处理异步时序。追问命令队列和 Promise 链有什么区别?命令队列在 .then() 之前不会执行,是同步入队异步执行;Promise 链是立即执行。所以不能把 Cypress 命令赋值给变量:const el = cy.get('#btn') 拿到的是 chainable 不是元素,必须用 .then() 回调取值。什么时候需要用 .then()?需要访问命令返回值或混合同步逻辑时。比如从响应中提取 ID 再构造下一个请求。注意 .then() 内部的 cy 命令会重新入队,不会立即执行。自动等待超时了怎么办?可通过 { timeout: 10000 } 单独设置,或在 cypress.config.js 中配置 defaultCommandTimeout 全局调整。超时后命令失败,测试中断并截图。应优先用 should() 断言替代加大超时。cy.wait() 和自动等待什么时候用?自动等待覆盖 DOM 和 XHR 场景,一般够用。但 cy.intercept() 拦截请求后需 cy.wait('@alias') 确保请求完成再断言响应,这是显式等待的典型场景。为什么不能在 .then() 外用 async/await?Cypress 命令不在 Promise 上运行,await 一个 chainable 不会等命令执行完。混用 async/await 会导致时序错乱,Cypress 官方明确不推荐在命令链中使用 async/await。写段代码cy.intercept('GET', '/api/users').as('users');cy.visit('/dashboard');cy.wait('@users').its('response.statusCode').should('eq', 200);cy.get('#user-list').should('be.visible');
服务端阅读 05月29日 00:52

Cypress 和 Selenium 有什么区别?何时选择 Cypress?

核心区别在架构:Cypress 运行在与应用同源的浏览器内,通过 Chrome DevTools Protocol 直接操作 DOM,内置自动等待和重试机制;Selenium 通过外部 WebDriver 进程与浏览器通信,需显式编写等待逻辑。这意味着 Cypress 调试体验远优于 Selenium(可视化 Test Runner、时间旅行),且代码更简洁,但仅支持 Chromium 内核和 JavaScript;Selenium 跨浏览器覆盖全面(Chrome/Firefox/Safari),支持多语言(Java/Python/C#),适合需要兼容性测试的团队。选择 Cypress 的场景:前端 SPA 项目为主、团队用 JavaScript、追求快速反馈和低维护成本。选 Selenium 的场景:必须覆盖多浏览器、团队非 JS 技术栈、需测试非 Web 应用。追问Cypress 的同源架构为什么无法测试跨域场景?有什么变通方案?Selenium 的显式等待(WebDriverWait)和隐式等待(implicit wait)有什么区别?各自的风险是什么?Cypress 的 cy.intercept() 如何模拟后端响应?与 Selenium 的 Mock Server 方案相比优劣如何?大型项目中 Cypress 测试执行变慢,如何优化?Playwright 与 Cypress 相比有哪些改进?是否正在取代 Cypress?写段代码// Cypress: 自动等待,无需 sleepcy.visit('/login');cy.get('#user').type('admin');cy.get('#pass').type('1234');cy.get('#submit').click();cy.url().should('include', '/dashboard');// Selenium (Python): 必须显式等待from selenium.webdriver.support.ui import WebDriverWaitelem = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, 'submit')))elem.click()
服务端阅读 05月29日 00:25

Cypress 中如何实现数据驱动测试?

数据驱动测试将测试数据与逻辑分离,Cypress 通过 cy.fixture() 加载 cypress/fixtures/ 下的 JSON 文件驱动测试。核心流程:在 fixtures 目录建数据文件,测试中用 cy.fixture() 加载后遍历执行,实现一组逻辑跑多组数据。更简洁的方式是结合 cy.each() 或原生 forEach 迭代数据,避免为每组数据写重复测试。外部数据文件适合管理多环境配置和边界值数据集,fixtures 适合静态模拟数据。追问cy.fixture() 和直接 import JSON 有什么区别?cy.fixture() 走 Cypress 管道,支持超时重试和命令日志;import 是编译时加载,不经过 Cypress 命令链,无法在报告中追踪。如何用 fixtures 实现参数化测试?用 cy.fixture() 加载数组数据,配合 cy.each() 或 forEach 遍历,每组数据生成独立 it 用例,失败时能精确定位是哪组数据有问题。fixtures 数据在不同测试间会互相影响吗?Cypress 默认每个测试前重置 fixtures 状态;但如果在 before 中修改 fixture 返回的对象,会影响后续测试,建议每次加载用深拷贝。大量测试数据该怎么管理?按模块分目录(fixtures/auth/、fixtures/products/),公共数据放 fixtures/common/;环境相关数据用 cypress.env.json + CYPRESS_ 环境变量区分。写段代码// fixtures/users.json: [{"name":"Alice","role":"admin"},{"name":"Bob","role":"user"}]describe('数据驱动权限测试', () => { let users; before(() => { cy.fixture('users').then(data => users = data); }); users.forEach((user, i) => { it(`用户 ${user.name} 角色为 ${user.role}`, () => { cy.login(user.name, 'pass'); cy.get('[data-testid=role]').should('contain', user.role); }); });});
服务端阅读 05月29日 00:24

Cypress 中 Page Object 模式有必要用吗?

Page Object 模式将页面元素选择器和操作封装为独立类,测试代码只调用方法不直接写选择器,页面变更时只需改 Page Object 不改测试。但在 Cypress 中,Custom Command 常能替代 POM 的大部分功能——cy.login() 比 loginPage.login() 更符合 Cypress 风格。POM 真正有价值的场景是:多页面复杂流程(如电商下单流程跨 4 个页面)、团队已熟悉 POM 模式、选择器需要跨多个测试文件共享复用。追问Cypress 官方对 POM 的态度是什么?官方认为 POM 不是必须的,Cypress 的 Custom Command 和组合式 API 已能很好复用逻辑;过度封装反而增加维护成本,简单场景用 Custom Command 更合适。Custom Command 和 POM 怎么选?单页面或少交互场景用 Custom Command(如 cy.login());多页面流程且团队习惯 OOP 风格时用 POM,两者可混合使用。POM 中选择器应该怎么管理?统一使用 data-testid 属性作为选择器锚点,不依赖 CSS class 或 DOM 结构,UI 样式变更不影响测试稳定性。POM 类变得臃肿怎么办?拆分为基础 PageObject(通用方法)+ 具体页面子类;组件级别的对象(如导航栏)独立为 Component Object,避免单类膨胀。写段代码// POM 类 + 测试使用class LoginPage { get username() { return cy.get('[data-testid=username]'); } get password() { return cy.get('[data-testid=password]'); } login(user, pass) { this.username.type(user); this.password.type(pass); cy.get('[data-testid=submit]').click(); }}// 测试中const login = new LoginPage();login.login('admin', '123456');cy.url().should('include', '/dashboard');
服务端阅读 05月29日 00:24

如何优化 Cypress 测试的执行速度?

核心优化手段:用 cy.session() 缓存登录状态避免重复登录;通过 --parallel 并行执行拆分 spec 文件;用 cy.intercept() 拦截 mock 网络请求减少真实 API 调用;避免 cy.wait() 硬编码等待,让 Cypress 自动重试机制生效;配置 baseUrl 避免重复导航。综合使用可将 1000+ 用例执行时间从 20 分钟压到 5 分钟以内。追问cy.session() 和 before() 中登录有什么区别?before() 每个测试文件都会执行登录;cy.session() 在同文件内跨测试复用登录状态,且 session 失效时自动重建,减少冗余请求。并行执行为什么需要 Cypress Cloud?Cypress 的并行调度依赖 Dashboard 服务分配测试到不同机器,免费版可用 cypress-parallel 插件做本地并行,但缺少负载均衡。如何识别最慢的测试用例?运行 cypress run --reporter=json 生成报告,按 duration 排序定位瓶颈;或在 Cypress Cloud 查看耗时分布图。spec 文件应该怎么拆分?按功能模块拆分,每个 spec 控制在 10-20 个测试;避免单文件过大影响并行均衡,也避免过碎导致启动开销占比过高。cy.intercept() mock 数据会不会导致测试失真?会,应在关键流程用真实 API,仅在辅助请求(如第三方服务)使用 mock,并在 CI 中定期跑无 mock 的全量回归验证。写段代码// cy.session 缓存登录 + intercept mockbeforeEach(() => { cy.session('user', () => { cy.intercept('POST', '/api/login', { statusCode: 200 }); cy.visit('/login'); cy.get('[name=email]').type('user@test.com'); cy.get('[name=password]').type('pass123'); cy.get('button').click(); });});
服务端阅读 05月29日 00:24

Cypress 测试隔离和数据管理怎么做?

Cypress 默认每个 it 块前会重置浏览器状态(清 cookie、localStorage、sessionStorage),Cypress 12+ 开启 testIsolation: true 后更强——每次测试前自动 cy.visit() 恢复初始页面。数据管理分三层:fixtures 管理静态数据、cy.request() + 自定义命令做动态数据创建、cy.task() 操作数据库清理。核心原则:测试不依赖执行顺序,每个测试自给自足。追问cy.session() 怎么用?和 beforeEach 中登录有什么区别?cy.session() 缓存登录后的 cookie 和 localStorage,同一 spec 内重复使用时不重新登录,显著加速测试。而 beforeEach 每次都执行完整登录流程。session 在 spec 间不共享(Cypress 12+ 的限制),跨 spec 需配合 cy.request 预置状态。testIsolation: true 和 false 各适合什么场景?true(默认)适合功能测试,保证每个用例干净状态;false 适合需要跨测试保持状态的端到端流程测试(如多步向导),但需手动在 beforeEach 中清理关键状态。fixtures 和 cy.task() 生成数据怎么选?fixtures 适合不变的测试输入(表单数据、API 响应模板);cy.task() 适合需要与后端交互的动态数据(创建测试用户、插入数据库记录),task 在 Node 环境执行,能直连数据库。如何保证并行执行时测试数据不冲突?用唯一标识生成数据:Date.now() 或 Cypress._.random(),避免固定用户名。测试结束在 afterEach 中通过 cy.request 或 cy.task 清理自己创建的数据,不依赖全局 reset。写段代码// cy.session 加速登录 + fixtures 管理数据Cypress.Commands.add('login', (role) => { cy.session(role, () => { cy.fixture('users').then((u) => { cy.request('POST', '/api/login', u[role]); }); });});
服务端阅读 05月29日 00:24

Cypress 和 Selenium 有什么核心区别?

Cypress 直接运行在浏览器内部,通过 Chrome DevTools API 与页面通信,无需 WebDriver 中间层;Selenium 通过外部 WebDriver 进程以 HTTP 协议控制浏览器,架构上多了一跳延迟。Cypress 自动重试断言、内置时间旅行调试、仅支持 Chromium 系浏览器;Selenium 支持所有主流浏览器但需手动处理显式等待。选择依据:前端 SPA 项目选 Cypress 快速反馈,跨浏览器兼容测试选 Selenium。追问Cypress 的自动等待机制和 Selenium 的显式等待有什么区别?Cypress 在断言失败时自动重试(默认 4 秒),无需手动写 wait;Selenium 必须用 WebDriverWait 显式等待元素出现,否则直接抛异常。Cypress 为什么不支持跨域和多标签页?Cypress 运行在同源策略下,跨域需 cy.origin() 处理,多标签页通过模拟而非真正打开新窗口,这是架构上的硬限制。Selenium Grid 和 Cypress Cloud 的并行策略有何不同?Selenium Grid 自建节点分发测试到不同浏览器,免费可控;Cypress Cloud 依赖官方服务按机器数并行,免费版有限制。两者在 CI/CD 中如何选择?小团队前端项目用 Cypress 开发体验好、上手快;大型项目需 Firefox/Safari 兼容性验证时,Selenium 更合适,也可混合使用。写段代码// Cypress: 自动等待,无需显式 waitcy.get('#username').type('user');cy.get('#password').type('pass');cy.get('button').click();cy.url().should('include', '/dashboard');// Selenium: 必须显式等待WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, 'username')));
服务端阅读 05月29日 00:24

Cypress 视觉回归测试怎么做?

Cypress 本身不内置视觉回归功能,需借助插件实现:cypress-image-diff(轻量免费)、Percy/Chromatic(云端对比平台,付费)。核心流程是首次运行生成基准截图,后续运行做像素级 diff,差异超过阈值则判定失败。关键配置是 threshold 容忍度和动态内容排除策略。追问cypress-image-diff 和 Percy 怎么选?cypress-image-diff 本地运行、零费用、适合小团队,截图存仓库;Percy 提供云端可视化审阅、多浏览器快照、PR 集成审批流,适合中大型团队。Percy 还能自动处理抗锯齿和字体渲染差异。动态内容(日期、轮播图)导致误报怎么处理?三种策略:1) 截图前用 CSS 隐藏动态区域 cy.get('.carousel').invoke('css', 'visibility', 'hidden');2) 插件的 ignore 区域配置;3) 用 cy.clock() 冻结时间,使时间戳固定。threshold 阈值怎么设定?像素级对比用 0.01-0.05(严格),感知对比用 0.1-0.2(宽松)。建议核心页面 0.01,次要页面 0.1。首次跑测试建立 baseline 后再微调。CI 环境中截图不一致怎么解决?CI 和本地渲染差异(字体、GPU)导致误报。方案:1) Docker 统一运行环境;2) 只在 CI 中做视觉测试;3) 用 Percy 等云端工具消除本地差异;4) 禁用动画和字体反锯齿。写段代码// cypress-image-diff 基本用法cy.compareSnapshot('homepage', 0.02);// 排除动态区域cy.get('.live-data').invoke('css', 'visibility', 'hidden');cy.compareSnapshot('dashboard', 0.05);