Skip to main content
Cypress应用

常见问题

一般性问题

Cypress 是免费开源的吗?

Cypress App 是一个免费、可下载且开源(MIT 许可证)的应用程序,始终免费使用。

Cypress Cloud 是一个提供多种计费方案(包括免费开源计划)的 Web 应用程序,用于在 CI 中记录测试运行。

此外,我们还提供高级解决方案,如 UI CoverageCypress Accessibility,它们有独立的定价。

详情请参阅我们的 定价页面

支持哪些操作系统?

您可以在 Mac、Linux 和 Windows 上 安装 Cypress。更多信息请参阅 系统要求

是否支持原生移动应用?

Cypress 永远无法在原生移动应用上运行,但可以测试移动网页浏览器的某些功能,以及测试在浏览器中开发的移动应用,例如使用 Ionic 框架

目前,您可以使用 cy.viewport() 命令控制视口,测试网站或 Web 应用中的响应式移动视图。您还可以使用 自定义命令 模拟某些行为,如滑动。

您可以阅读 这篇文章 了解如何使用 Ionic 和 Cypress 测试移动应用,并在 Cypress Real World App 中查看我们如何管理测试移动视口。

与其他测试工具有何不同?

Cypress App 是一个集成了应用程序、框架和服务的混合体。它借鉴了其他测试工具的一些优点,将它们整合并改进。

除了 App 之外,Cypress 还提供 Cypress CloudUI CoverageAccessibility 等解决方案,以扩展测试的价值并获取测试套件的健康状况。

您可以在我们的 从 Selenium 迁移从 Protractor 迁移 指南中查看我们对一些框架的评估。

是否支持 X 语言或 X 框架?

全部支持。Ruby、Node、C#、PHP 等都不重要。Cypress 可以测试任何在浏览器环境中运行的内容。它与后端、前端、语言和框架无关。

您需要用 JavaScript 或 TypeScript 编写测试,除此之外,Cypress 可以在任何地方工作。

是否支持我的 CI 提供商?

Cypress 支持任何 CI 提供商

是否需要修改现有代码?

不需要。但如果您想测试应用中不易测试的部分,您可能需要重构这些部分(就像任何测试一样)。

我们使用 WebSocket,Cypress 能支持吗?

可以。

能否在 .jspa 上使用 Cypress?

可以。Cypress 可以测试任何渲染到浏览器的内容。

能否用 Cypress 在外部网站(如 gmail.com)上模拟用户操作?

使用 Cypress 测试第三方应用并非其设计用途。它 可能 有效,但违背了其创建的初衷。Cypress 用于 开发 您的应用时帮助编写测试。

是否有代码覆盖率?

有一个插件和详细文档,介绍如何获取端到端、单元和全栈代码覆盖率。

是否有我的语言的驱动绑定?

Cypress 使用 WebDriver 进行测试,因此它不使用也没有任何驱动绑定的概念。如果您的语言可以以某种方式转译为 JavaScript,则可以配置 Cypress webpack 预处理器Cypress Browserify 预处理器 将测试转译为 Cypress 可以运行的 JavaScript。

Spec、Test 和 Test Suite 之间有什么区别?

在 Cypress 中,spectesttest suite 有特定的含义:

Spec 一个包含一组测试用例或单个测试的测试文件。每个 spec 文件通常专注于测试应用的某个特定功能或方面。Spec 定义了测试场景、与应用的交互以及验证预期行为的断言。Spec 文件用 JavaScript 或 TypeScript 编写,扩展名为 .js.ts

Test Spec 文件中的一个独立测试用例。它代表需要测试的特定场景或行为。测试通常包括对被测应用执行的一系列操作,如与 UI 元素交互、提交表单或验证预期结果的断言。Cypress 提供了一组丰富的内置命令和 API,以声明式的方式编写测试和与应用交互。

Test Suite 一组 spec 文件的集合。它允许您根据功能区域、模块或特定功能等标准组织测试。一个测试套件可以包含多个 spec 文件,每个文件包含一个或多个测试。通过将相关测试分组到测试套件中,您可以更有效地组织和管理测试。Cypress 提供了在测试执行期间运行单个 spec 文件、多个 spec 文件或整个测试套件的选项。

何时编写端到端测试,何时编写组件测试?

关于如何选择 Cypress 测试类型,我们推荐阅读 测试类型指南

何时编写单元测试,何时编写端到端测试?

我们认为单元测试和端到端测试有区别,应根据情况选择:

单元测试端到端测试
关注代码关注功能
应保持简短可以较长
检查操作的返回结果检查操作的副作用:DOM、存储、网络、文件系统、数据库
对开发者工作流重要对最终用户工作流重要

此外,以下是一些经验法则:

  • 如果代码被其他代码调用,使用单元测试。
  • 如果代码将被外部系统(如浏览器)调用,使用端到端测试。
  • 如果单元测试需要大量模拟,并且您需要使用 jsdomenzymesinon.js 等工具模拟真实环境,您可能希望将其重写为端到端测试。
  • 如果端到端测试 通过浏览器,而是直接调用代码,您可能希望将其重写为单元测试。

最后,单元测试和端到端测试并非对立,而是工具箱中的互补工具。

如何说服公司使用 Cypress?

首先,诚实地评估 Cypress 是否适合 您的公司和项目。我们相信最佳方法是“自下而上”的方法,即展示 Cypress 如何解决公司的特定需求。在项目中实现一个原型,测试几个常见的用户场景,识别是否存在技术障碍,并向他人展示原型。如果您能向其他工程师展示 Cypress 作为开发工具的优势,那么它可能会更快被采纳。

如何获取 Cypress 新版本的信息?

我们在 GitHub 和 npm 上发布版本,同时发布包含主要变更、修复和更新的变更日志。您可以通过以下链接关注:

Cypress 版本发布频率如何?

我们尝试每两周发布一次 Cypress App。

如果有重大 bug 超出发布计划,我们会尽快发布补丁。

使用 Cypress App 时捕获或传输哪些信息?

Cypress App 在本地运行,因此不会向 Cypress 发送任何数据,除了异常数据(可以按照 此处 的说明禁用)。

能否用 Cypress 编写 API 测试?

Cypress 主要用于端到端和组件测试,但如果您需要编写一些使用 cy.request() 命令调用后端 API 的测试……谁能阻止您呢?

it('adds a todo', () => {
cy.request({
url: '/todos',
method: 'POST',
body: {
title: 'Write REST API',
},
})
.its('body')
.should('deep.contain', {
title: 'Write REST API',
completed: false,
})
})

查看我们的 真实世界应用(RWA),其中使用了许多此类测试来验证后端 API。

您可以将 UI 命令与 API 测试结合使用:

it('adds todos', () => {
// 通过 UI 驱动应用
cy.visit('/')
cy.get('.new-todo')
.type('write E2E tests{enter}')
.type('add API tests as needed{enter}')
// 确认服务器有 2 个待办项
cy.request('/todos')
.its('body')
.should('have.length', 2)
.and((items) => {
// 验证返回的项
})
})

编写针对性 API 测试的好策略是使用它们覆盖其他测试未覆盖的难以测试的代码。您可以使用 代码覆盖率 作为指南找到这些地方。

操作指南问题

如何获取元素的文本内容?

Cypress 命令返回 jQuery 对象,因此可以调用其方法。

如果要断言元素的文本内容:

cy.get('div').should('have.text', 'foobarbaz')

如果文本包含 非换行空格 实体  ,则使用 Unicode 字符 \u00a0 代替  

<div>Hello&nbsp;world</div>
cy.get('div').should('have.text', 'Hello\u00a0world')

也可以使用 cy.contains 命令,它处理非换行空格实体:

cy.contains('div', 'Hello world')

如果需要在断言之前处理文本:

cy.get('div').should(($div) => {
const text = $div.text()

expect(text).to.match(/foo/)
expect(text).to.include('foo')
expect(text).not.to.include('bar')
})

如果需要将文本转换为数字并检查是否大于 10:

cy.get('div').invoke('text').then(parseFloat).should('be.gt', 10)

如果需要保留引用或比较文本值:

cy.get('div')
.invoke('text')
.then((text1) => {
// 在此处执行更多操作

// 点击更改 div 文本的按钮
cy.get('button').click()

// 再次获取 div 并比较前后文本
cy.get('div')
.invoke('text')
.should((text2) => {
expect(text1).not.to.eq(text2)
})
})

jQuery 的 .text() 方法在底层自动调用 elem.textContent。如果想使用 innerText,可以这样做:

cy.get('div').should(($div) => {
// 访问原生 DOM 元素
expect($div.get(0).innerText).to.eq('foobarbaz')
})

这相当于 Selenium 的 getText() 方法,返回可见元素的 innerText。

如何获取输入框的值?

Cypress 返回 jQuery 对象,因此可以调用其方法。

如果要断言输入框的值:

cy.get('input').should('have.value', 'abc')

如果需要在断言之前处理文本:

cy.get('input').should(($input) => {
const val = $input.val()

expect(val).to.match(/foo/)
expect(val).to.include('foo')
expect(val).not.to.include('bar')
})

如果需要保留引用或比较值:

cy.get('input')
.invoke('val')
.then((val1) => {
// 在此处执行更多操作

// 点击更改输入框值的按钮
cy.get('button').click()

// 再次获取输入框并比较前后值
cy.get('input')
.invoke('val')
.should((val2) => {
expect(val1).not.to.eq(val2)
})
})

如何比较一个事物的值与状态?

我们的 变量和别名指南 提供了相关示例。

能否将属性值存储在常量或变量中供以后使用?

可以,有几种方法可以实现。一种方法是使用 闭包。通常,用户认为需要将值存储在 constvarlet 中。Cypress 建议仅在处理可变对象(状态会变化)时这样做。

有关示例,请阅读我们的 变量和别名指南

如何获取使用 Cypress 找到的元素的原生 DOM 引用?

Cypress 将元素包装在 jQuery 中,因此可以在 .then() 命令中获取原生元素:

cy.get('button').then(($el) => {
$el.get(0)
})

如果元素不存在,如何执行不同的操作?

您询问的是条件测试和控制流。

请阅读详细的 条件测试指南

如何让 Cypress 等待 DOM 中的某些内容可见?

info
记住

基于 DOM 的命令会自动 重试 并在失败前等待对应的元素存在。

Cypress 提供了许多强大的方式 查询 DOM,所有方式都带有重试和超时逻辑。

另一种等待元素出现在 DOM 中的方法是使用 timeout。Cypress 命令的默认超时为 4 秒,但大多数 Cypress 命令允许 自定义超时选项。超时可以全局配置或按命令配置。

某些情况下,您的 DOM 元素可能无法操作。Cypress 提供了一个强大的 {force:true} 选项,可以传递给大多数操作命令。

请阅读我们的 Cypress 核心概念介绍。这是理解如何使用 Cypress 进行测试的最重要指南。

如何等待应用加载?
仅限端到端测试

我们见过这个问题的许多不同版本。答案因应用行为和测试环境而异。以下是这个问题的一些最常见版本。

如何知道页面是否加载完成?

使用 cy.visit() 加载应用时,Cypress 会等待 load 事件触发。cy.visit() 命令加载远程页面,并在所有外部资源完成加载阶段之前不会解析。因为我们预计您的应用加载时间不同,此命令的默认超时设置为 60000ms。如果您访问无效 URL 或 每个测试需要不同源的第二个唯一域,Cypress 会记录详细但友好的错误消息。

在 CI 中,如何确保服务器已启动?

您可以尝试以下模块:

如何等待请求完成?

推荐的方法是使用 cy.intercept() 定义路由,在访问之前为这些路由创建 别名,然后可以使用 cy.wait() 明确告诉 Cypress 要等待哪些路由。没有神奇的方法可以等待所有 XHR 或 Ajax 请求。 由于这些请求的异步性质,Cypress 无法直观地等待它们。您必须定义这些路由并明确告诉 Cypress 要等待哪些请求。

能否使用 Cypress 限制网络速度?

您可以通过开发者工具的 Network 面板限制网络连接。此外,您可以通过从 Network Conditions 抽屉中选择 Custom > Add 添加自定义预设。

我们目前不提供在 cypress run 期间模拟此功能的选项。

能否使用 ES7 async / await 语法?

不能。命令 API 目前的设计不支持此功能。要理解 Cypress 命令的工作原理,请阅读:

如果应用使用动态类或动态 ID,如何选择或查询元素?

阅读更多关于 选择元素的最佳实践

只想在特定文件夹中运行测试,如何实现?

您可以在 cypress run 期间通过 --spec 标志传递 glob 来指定要运行的测试文件。您可以传递匹配要运行的测试所在文件夹的 glob。

此功能仅在 cypress run 时可用。

是否有建议的方法或最佳实践来定位元素或编写元素选择器?

有。阅读更多关于 选择元素的最佳实践

能否阻止 Cypress 在应用抛出未捕获异常错误时使测试失败?

可以。默认情况下,Cypress 会在未捕获异常从应用中冒出时自动使测试失败。

Cypress 为此(以及其他许多事件)暴露了一个事件,您可以监听以:

  • 调试错误实例
  • 阻止 Cypress 使测试失败

这在 事件目录 页面和 处理错误 配方中有详细记录。

Cypress 是否在应用有未处理的拒绝 Promise 时使测试失败?

默认情况下不会,Cypress 不监听应用中的未处理 Promise 拒绝事件,因此不会使测试失败。您可以设置自己的监听器并使测试失败,参见我们的配方 处理错误

// 在 cy.visit 期间注册监听器
it('fails on unhandled rejection', () => {
cy.visit('/', {
onBeforeLoad(win) {
win.addEventListener('unhandledrejection', (event) => {
const msg = `UNHANDLED PROMISE REJECTION: ${event.reason}`

// 使测试失败
throw new Error(msg)
})
},
})
})

// 替代方案:为此测试注册监听器
it('fails on unhandled rejection', () => {
cy.on('window:before:load', (win) => {
win.addEventListener('unhandledrejection', (event) => {
const msg = `UNHANDLED PROMISE REJECTION: ${event.reason}`

// 使测试失败
throw new Error(msg)
})
})

cy.visit('/')
})

// 替代方案:在每个测试中注册监听器
before(() => {
Cypress.on('window:before:load', (win) => {
win.addEventListener('unhandledrejection', (event) => {
const msg = `UNHANDLED PROMISE REJECTION: ${event.reason}`

// 使测试失败
throw new Error(msg)
})
})
})

it('fails on unhandled rejection', () => {
cy.visit('/')
})

如何覆盖环境变量或为不同环境创建配置?

可以通过环境变量、CLI 参数、JSON 文件等方式向 Cypress 传递配置。

阅读环境变量指南。

能否覆盖或更改浏览器使用的默认用户代理?

可以。

  • Cypress 建议在配置文件中将 userAgent 设置为 配置值
  • 可以通过在 cy.visit()cy.request() 的选项中设置 user-agent 标头来伪造 单个 请求的 userAgent,但这不会在浏览器中传播 userAgent,可能导致应用渲染异常。

能否阻止流量发送到特定域?我想阻止 Google Analytics 或其他提供商。

可以。可以在 Cypress 配置中使用 blockHosts

此外,查看我们的 Stubbing Google Analytics 配方

如何验证对 Google Analytics 等分析工具的调用是否正确?

可以存根它们的函数,然后确保它们被调用。

查看我们的 Stubbing Google Analytics 配方

我正在测试聊天应用。能否同时运行多个浏览器?

我们在此详细回答了这个问题。

如何修改或传递用于启动浏览器的参数?

使用 before:browser:launch 插件事件。

能否让 cy.request() 轮询直到满足条件?

可以。您可以像 其他递归循环 一样操作。

能否使用 Page Object 模式?

可以。

如果想抽象行为或汇总一系列操作,可以使用我们的 API 创建可重用的 自定义命令。也可以使用 JavaScript 函数实现这一点。

对于想使用页面对象的人,我们强调了 最佳实践 来复制页面对象模式。

能否运行单个测试或一组测试?

可以通过在测试套件或特定测试上放置 .only 来运行一组测试或单个测试。

可以通过向 cypress run 传递 --spec 标志来运行单个测试文件或一组测试。

如何测试文件上传?

可以在应用中上传文件,但具体方法取决于上传代码的编写方式。.selectFile() 命令 详细介绍了各种选项,但在许多情况下,最简单的选项会起作用:

cy.get('[data-cy="file-input"]').selectFile('cypress/fixtures/data.json')

如何检查邮件是否已发送?

caution
反模式

不要尝试使用 UI 检查邮件。相反,选择以编程方式使用第三方 API 或直接与服务器通信。阅读 最佳实践

  1. 如果应用在本地运行并通过 SMTP 服务器直接发送邮件,可以在 Cypress 中使用临时本地测试 SMTP 服务器。阅读博客文章 "使用 Cypress 测试 HTML 邮件" 获取详细信息。
  2. 如果应用使用第三方邮件服务,或者无法存根 SMTP 请求,可以使用具有 API 访问权限的测试邮件收件箱。阅读博客文章 "使用 SendGrid 和 Ethereal 账户全面测试 HTML 邮件" 获取详细信息。

Cypress 甚至可以在浏览器中加载收到的 HTML 邮件,验证邮件的功能和视觉样式:

测试找到并点击确认注册按钮
  1. 可以使用提供测试用临时邮件地址的第三方邮件服务。其中一些服务甚至提供 Cypress 插件 来访问邮件。

如何等待对同一 URL 的多个请求?

应设置别名(使用 .as())到匹配所有 XHR 的单个 cy.intercept()。然后可以多次 cy.wait() 它。Cypress 会跟踪有多少匹配的请求。

cy.intercept('/users*').as('getUsers')
cy.wait('@getUsers') // 等待第一次 GET /users/
cy.get('#list>li').should('have.length', 10)
cy.get('#load-more-btn').click()
cy.wait('@getUsers') // 等待第二次 GET /users/
cy.get('#list>li').should('have.length', 20)

如何初始化/重置数据库?

可以使用 cy.request()cy.exec()cy.task() 与后端通信以初始化数据。

也可以使用 cy.intercept() 直接存根请求,完全避免处理数据库。

如何测试 iframe 内的元素?

我们有一个 开放提案 扩展 API 以支持“切换进入” iframe 然后再切换出来。

默认情况下,Cypress 自动 在每个测试之前清除所有 cookie、localStorage 和 session 以防止状态泄漏。

可以使用 cy.session() 命令在测试之间保留会话详情。

或者,可以在套件或 e2e 配置级别禁用 testIsolation 以防止清除浏览器状态。

某些元素有动画效果,如何解决?

通常可以通过断言 .should('be.visible')其他断言 来应对动画。

// 假设点击事件触发动画
cy.get('.element').click().should('not.have.class', 'animating')

如果动画特别长,可以通过增加断言前命令的 timeout 来延长 Cypress 等待断言通过的时间。

cy.get('button', { timeout: 10000 }) // 等待最多 10 秒让此按钮存在
.should('be.visible') // 并且可见

cy.get('.element')
.click({ timeout: 10000 })
.should('not.have.class', 'animating')
// 等待最多 10 秒让 .element 没有 'animating' 类

然而,大多数时候您甚至不必担心动画。为什么?Cypress 会 自动等待 元素停止动画,然后通过 .click().type() 等操作命令与之交互。

能否测试在新标签页中打开的锚链接?

Cypress 本身不支持多标签页,但可以使用 @cypress/puppeteer 插件 测试新标签页。

如果想原生测试锚链接,有许多解决方法可以在应用中测试它们。

阅读关于标签处理和链接的配方,了解如何测试锚链接。

能否动态测试多个视口?

可以。我们提供了一个 示例

能否在多个子域上运行相同的测试?
仅限端到端测试

可以。在此示例中,我们循环遍历 URL 数组并对徽标进行断言。

const urls = ['https://docs.cypress.io', 'https://www.cypress.io']

describe('Logo', () => {
urls.forEach((url) => {
it(`Should display logo on ${url}`, () => {
cy.visit(url)
cy.get('#logo img').should('have.attr', 'src').and('include', 'logo')
})
})
})
动态 URL 测试的命令日志

如何在 Cypress 中 require 或导入 node 模块?

您在 Cypress 中编写的代码在浏览器中执行,因此可以导入或 require JS 模块, 仅限于那些在浏览器中工作的模块。

可以像往常一样 requireimport 它们。我们使用 webpack 和 Babel 预处理您的 spec 文件。

我们建议使用以下方法之一在浏览器外部执行代码:

查看“Node 模块”示例配方。

能否为代理提供适当的 SSL 证书,使页面不显示“不安全”?

不能。Cypress 实时修改网络流量,因此必须位于服务器和浏览器之间。我们无法以其他方式实现这一点。

能否检测应用是否在 Cypress 下运行?

可以在 应用代码 中检查 window.Cypress 是否存在。

示例:

if (window.Cypress) {
// 我们在 Cypress 中运行
// 在此处执行不同的操作
window.env = 'test'
} else {
// 我们在普通浏览器中运行
}

如果想检测 Node.js 代码是否在 Cypress 中运行,Cypress 会设置一个操作系统级环境变量 CYPRESS=true。可以通过查找 process.env.CYPRESS 来检测是否在 Cypress 中运行。

能否测试文件是否已下载?我想测试按钮点击是否触发下载。

有许多方法可以测试这一点,具体取决于情况。您需要了解实际导致下载的原因,然后思考测试该机制的方法。

如果服务器发送特定的 disposition 标头导致浏览器提示下载,可以找出此请求的 URL,并使用 cy.request() 直接访问它。然后可以测试服务器是否发送了正确的响应标头。

如果是锚点触发的下载,可以测试其 href 属性是否正确。只要可以验证点击按钮会发出正确的 HTTP 请求,可能就足以测试。

最后,如果想真正下载文件并验证其内容,请参阅我们的 文件下载 配方。

最终,您需要了解您的实现并测试足够的内容以覆盖所有内容。

能否捕获 Cypress 中的 Promise 链?

不能。无法为失败的命令添加 .catch 错误处理程序。阅读更多关于 Cypress 命令不是 Promise 的信息

能否修改截图/视频的分辨率?

有一个 开放问题 以便更轻松地配置此功能。

可以在无头运行时使用 此解决方法 修改截图和视频大小。

Cypress 是否支持 ES7?

支持。可以使用我们的 预处理器插件 之一或 编写自己的自定义预处理器 来自定义 spec 的处理方式。

通常可以重用现有的 Babel 和 webpack 配置。

如何确定 Cypress 的最新版本?

有几种方法。

直接访问我的站点时证书已验证,但通过 Cypress 启动的浏览器显示为“不安全”。为什么?

使用 Cypress 测试 HTTPS 站点时,可能会在浏览器 URL 旁边看到警告。这是正常的。Cypress 修改了服务器和浏览器之间的流量。浏览器注意到这一点并显示证书警告。然而,这只是表面现象,不会以任何方式改变被测应用的运行方式,因此可以安全地忽略此警告。Cypress 和后端服务器之间的网络流量仍然通过 HTTPS 进行。

另请参阅 跨源测试 指南。

如何一起运行服务器和测试,然后关闭服务器?

要启动服务器、运行测试然后关闭服务器,我们推荐 这些 npm 工具

我发现了一个 bug!该怎么办?

  • 搜索现有的 开放问题,可能已经报告!
  • 更新 Cypress。您的问题可能 已经修复
  • 提交问题。快速解决 bug 的最佳机会是提供一个可以克隆和运行的可重现 bug 的仓库。

自定义命令和工具函数之间的正确平衡是什么?

自定义命令 指南中已经有一个很好的部分讨论了自定义命令和工具函数之间的权衡。我们通常认为可重用函数是更好的选择。此外,它们不会像自定义命令那样 混淆 IntelliSense

我的测试能否与 Redux / Vuex 数据存储交互?

通常,端到端测试通过公共浏览器 API(DOM、网络、存储等)与应用交互。但有时您可能希望对应用数据存储中保存的数据进行断言。Cypress 可以帮助您实现这一点。测试在同一个浏览器实例中运行,可以使用 cy.window 访问应用的上下文。通过有条件地从应用代码中暴露应用引用和数据存储,您可以让测试对数据存储进行断言,甚至通过 Redux 操作驱动应用。

对于组件测试,您对如何设置状态存储的提供程序和插件有更多控制。参见 Mount API 指南 了解在组件测试中使用存储的各种示例。

如何监视 console.log?

要监视 console.log,应使用 cy.stub()

cy.visit('/', {
onBeforeLoad(win) {
// 在此处存根函数
cy.stub(win.console, 'log').as('consoleLog')
},
})

// 其他测试代码

cy.get('@consoleLog').should('be.calledWith', 'Hello World!')

此外,查看我们的 Stubbing console 配方

如何在 cy.get() 中使用特殊字符?

根据 CSS 规范/. 等特殊字符是 id 的有效字符。

要测试 id 中包含这些字符的元素,需要使用 CSS.escapeCypress.$.escapeSelector 进行转义。

<!doctype html>
<html lang="en">
<body>
<div id="Configuration/Setup/TextField.id">Hello World</div>
</body>
</html>
it('test', () => {
cy.visit('index.html')
cy.get(`#${CSS.escape('Configuration/Setup/TextField.id')}`).contains(
'Hello World'
)

cy.get(
`#${Cypress.$.escapeSelector('Configuration/Setup/TextField.id')}`
).contains('Hello World')
})

注意 cy.$$.escapeSelector() 不起作用。cy.$$ 不引用 jQuery。它仅查询 DOM。了解更多原因

为什么 instanceof Event 不起作用?

可能是因为 Cypress App 中的 2 个不同窗口。更多信息,请查看 此处说明

如何阻止应用重定向到另一个 URL?

有时,您的应用可能会将浏览器重定向到另一个域,从而失去 Cypress 的控制。如果应用使用 window.location.replace 方法设置 相对 URL,可以尝试使用 experimentalSourceRewriting 选项中描述的 experimentalSourceRewriting 选项。

持续集成 (CI/CD)

为什么我的 Cypress 测试在本地通过但在 CI 中失败?

测试在 CI 中失败但在本地通过的原因有很多,包括:

  • 问题仅限于 Electron 浏览器(默认情况下 cypress run 在 Electron 浏览器中运行)
  • CI 构建过程中的错误可能导致测试失败
  • CI 中运行应用时的时序变化(例如,网络请求在本地超时内解析,但在 CI 中可能需要更长时间)
  • CI 与本地机器的差异——CPU 资源、环境变量等

要排查测试在 CI 中失败但在本地通过的原因,可以尝试以下策略:

  • 在本地使用 Electron 测试,确定问题是否特定于浏览器。
  • 在 CI 中使用 --browser 标志在不同的浏览器中运行,识别浏览器特定的问题。
  • 检查 CI 构建过程,确保没有更改应用导致测试失败。
  • 消除测试中的时间敏感性变化。例如,确保在查找依赖于网络请求数据的 DOM 元素之前,网络请求已完成。可以使用 别名 实现这一点。
  • 确保在 CI 运行中启用了视频录制和/或截图,并将录制与本地运行时的命令日志进行比较。

为什么 CI 中的视频录制冻结或丢帧?

如果在 CI 容器中运行测试时资源不足,录制的视频可能会冻结或丢帧。与任何应用程序一样,运行 Cypress 和录制视频需要足够的 CPU。可以 启用内存和 CPU 日志 运行测试,以识别和评估 CI 中的资源使用情况。

如果遇到此问题,建议切换到更强大的 CI 容器或提供商。

如果测试在 CI 中崩溃或挂起,该怎么办?

一些用户注意到,长时间运行的测试在 CI 上运行时更容易挂起甚至崩溃。当测试运行时间较长时,其命令和应用本身可能会分配比可用内存更多的内存,导致崩溃。崩溃的确切风险取决于应用和可用硬件资源。虽然没有单一的时间限制可以解决此问题,但通常建议将 spec 文件拆分为每个运行时间不超过一分钟的测试。

可以进一步拆分单个长时间运行的测试。例如,可以像 使用 App Actions 将非常长的 Cypress 测试拆分为较短的测试 中描述的那样,在单独的测试中验证较长用户功能的各个部分。

如何并行化运行?

可以在此处阅读更多关于并行化的信息 here

尝试在 CI 中安装 Cypress 时出现错误:EACCES: permission denied

首先,确保系统已安装 Nodenpm 是 Node 的全局包,默认在安装 Node 时安装,是安装我们的 cypress npm 包 所必需的。

接下来,检查您是否有安装权限,或者可能需要运行 sudo npm install cypress

是否有选项可以在 CI 中运行 Cypress 时打开开发者工具?我们想跟踪网络和控制台问题。

没有。目前无法在 cypress run 中运行 Cypress 时打开开发者工具。您可以使用 Cypress Test Replay 查看在 CI 中运行的测试的浏览器请求和控制台日志。

独特的 Cypress 用例

能否使用 Cypress 测试图表和图形?

可以。可以利用可视化测试工具验证图表和图形是否按预期渲染。更多信息,请查看 可视化测试指南

能否测试 Chrome 扩展?如何加载我的 Chrome 扩展?

可以。通过 在启动浏览器时加载它们 来测试扩展。

能否测试我的 Electron 应用?

测试 Electron 应用不会“直接工作”,因为 Cypress 设计用于测试浏览器中运行的任何内容,而 Electron 是浏览器 + Node。

尽管如此,我们使用 Cypress 通过存根 Electron 事件来测试我们自己的桌面应用前端。这些测试是开源的,您可以在此处查看 here

能否测试 HTML <head> 元素?

可以。在执行测试时,可以使用 cy.document() 在打开的控制台中查看整个 window.document 对象。甚至可以对 <head> 元素进行断言。查看此示例。

<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-Security-Policy" content="default-src 'self'" />
<meta name="description" content="This description is so meta" />
<title>Test the HEAD content</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body></body>
</html>
describe('The Document Metadata', () => {
beforeEach(() => {
cy.visit('/')
})

it('looks inside the head content using `cy.document()`', () => {
// 这将生成整个 window.document 对象
// 如果从命令日志中点击 DOCUMENT,
// 它将输出整个 #document 到控制台
cy.document()
})

// 或对 head 元素中的任何元数据进行断言

it('looks inside <title> tag', () => {
cy.get('head title').should('contain', 'Test the HEAD content')
})

it('looks inside <meta> tag for description', () => {
cy.get('head meta[name="description"]').should(
'have.attr',
'content',
'This description is so meta'
)
})
})

能否检查表单的 HTML 表单验证在输入无效时是否显示?

当然可以。

测试默认验证错误

<form>
<input type="text" id="name" name="name" required />
<button type="submit">Submit</button>
</form>
cy.get('[type="submit"]').click()
cy.get('input:invalid').should('have.length', 1)
cy.get('#name').then(($input) => {
expect($input[0].validationMessage).to.eq('Please fill out this field.')
})

测试自定义验证错误

<body>
<form>
<input type="email" id="email" name="email" />
<button type="submit">Submit</button>
</form>
<script>
const email = document.getElementById('email')

email.addEventListener('input', function (event) {
if (email.validity.typeMismatch) {
email.setCustomValidity('I expect an email!')
} else {
email.setCustomValidity('')
}
})
</script>
</body>
cy.get('input:invalid').should('have.length', 0)
cy.get('[type="email"]').type('not_an_email')
cy.get('[type="submit"]').click()
cy.get('input:invalid').should('have.length', 1)
cy.get('[type="email"]').then(($input) => {
expect($input[0].validationMessage).to.eq('I expect an email!')
})

能否用于基于模型的测试?

真实世界应用(RWA) 使用 XState 模型状态库实现。

能否用于性能测试?

Cypress 不是为性能测试而构建的。因为 Cypress 检测被测页面、代理网络请求并严格控制测试步骤,所以 Cypress 增加了自己的开销。因此,从 Cypress 测试中获得的性能数字比“正常”使用慢。尽管如此,您可以访问原生 window.performance 对象并获取页面时间测量,参见 评估性能指标 配方。

与其他工具/框架/库的集成

能否使用 Cypress 测试 React 应用?

对于端到端测试,绝对可以。一个完全测试的 React 应用的好例子是我们的 Cypress RealWorld App。您甚至可以在测试应用时使用 React DevTools,阅读 连接 Cypress 和 React DevTools 的最简单方法。如果确实需要通过名称、props 或状态选择 React 组件,请查看 cypress-react-selector

对于组件测试,我们支持 Vite、Webpack 和 Next.js 等多种框架用于 React 应用。参见 框架配置指南 获取更多信息。

能否使用 Jest 快照?

虽然 Cypress 中没有内置的 snapshot 命令,但可以创建自己的快照断言命令。阅读我们的博客文章 端到端快照测试 了解如何操作。我们推荐使用第三方模块 cypress-plugin-snapshots。对于其他快照插件,请搜索 插件 页面。

能否使用 Testing Library?

当然!可以添加 @testing-library/cypress 到您的设置中,并使用其方法如 findByRolefindByLabelTextfindByTextfindByTestId 等来查找 DOM 元素。

以下示例来自 Testing Library 的文档:

cy.findByRole('button', { name: /Jackie Chan/i }).click()
cy.findByRole('button', { name: /Button Text/i }).should('exist')
cy.findByRole('button', { name: /Non-existing Button Text/i }).should(
'not.exist'
)

cy.findByLabelText(/Label text/i, { timeout: 7000 }).should('exist')

// findAllByText _inside_ a form element
cy.get('form')
.findByText('button', { name: /Button Text/i })
.should('exist')

cy.findByRole('dialog').within(() => {
cy.findByRole('button', { name: /confirm/i })
})

能否使用 Cucumber 编写测试?

可以。

虽然 Cypress 不官方支持 Cucumber,但可以通过 社区插件 使用 Cucumber。使用插件会增加测试工作流程的复杂性,因此在采用 Cucumber 之前,请确保团队理解其优缺点。

如果团队希望直接在 Cypress 中使用 BDD 的 given/when/then 语法而不是 Cucumber 的场景,可能会对这些文章感兴趣:

能否使用 Cypress 检查 GraphQL 网络调用?

可以,使用较新的 API 命令 cy.intercept(),如 使用 GraphQL 中所述。

是否有 Cypress 的 ESLint 插件或全局变量列表?

有!查看我们的 ESLint 插件。它将设置运行 Cypress 所需的所有全局变量,包括浏览器全局变量和 Mocha 全局变量。

组件测试

什么是组件测试?

许多现代前端 UI 库鼓励使用小型、可重用的构建块(称为组件)编写应用。组件从小开始(如按钮、图像、输入框等),可以组合成更大的组件(如订单表单、日期选择器、菜单等),甚至整个页面。

组件测试是关于在隔离环境中测试单个组件,而不涉及应用的其余部分。这样只需关注组件的功能,而不必担心它如何适应整个页面。

Cypress 如何进行组件测试?

Cypress 会将组件挂载到一个空白画布中。这样做时,您可以直接访问组件的 API,更容易传入 props 或数据,并将组件置于特定状态。然后,您可以使用相同的 Cypress 命令、选择器和断言来编写测试。

Cypress 支持多种框架和开发服务器进行组件测试。

Cypress 组件测试与其他选项相比如何?

当 Cypress 挂载组件时,它是在实际浏览器中进行的,而不是像 jsdom 这样的模拟环境。这允许您在开发组件时直观地查看和交互。您可以使用与构建 Web 应用时相同的基于浏览器的开发者工具,如元素检查器、修改 CSS 和源代码调试。

Cypress 组件测试围绕与端到端测试相同的工具和 API 构建。任何熟悉 Cypress 的人都可以立即开始编写组件测试,而无需大量学习曲线。组件测试还可以使用庞大的 Cypress 生态系统、插件和服务(如 Cypress Cloud)来补充您的组件测试。

什么是 Mount 函数?

我们为每个 UI 库提供了一个从 cypress 包导入的 mount 函数。它负责在 Cypress 的沙盒 iframe 中渲染组件,并处理任何框架特定的清理工作。

// 示例展示在 react 中导入 mount 命令
import { mount } from 'cypress/react'

虽然可以在测试中使用 mount 函数,但我们建议使用 cy.mount(),它作为 自定义命令 添加到 cypress/support/component.js 文件中:

import { mount } from 'cypress/react'

Cypress.Commands.add('mount', mount)

这允许您在任何组件测试中使用 cy.mount(),而无需 import 框架特定的 mount 命令,并根据需要进行自定义。参见每个框架的示例指南,了解如何创建自定义 cy.mount() 命令。

为什么我的组件没有按预期渲染?

任何全局样式和字体必须导入并可供组件使用,就像在应用中一样。参见我们的 样式组件 指南获取更多信息。

为什么我的 spec 没有显示在 Specs 页面?

如果某些内容在 spec 列表中缺失,请确保文件具有 正确的扩展名且 specPattern 正确定义

如何修复 ESLint 错误,如使用全局 Cypress 对象?

如果在代码编辑器中遇到关于 Cypress 全局变量的 ESLint 错误,请安装 eslint-plugin-cypress ESLint 插件。

为什么 TypeScript 不识别全局 Cypress 对象或自定义 cypress 命令(如 cy.mount)?

在某些情况下,TypeScript 可能无法识别不在 cypress 目录中的 Cypress spec 文件中的自定义 cy.mount() 命令。在这种情况下,您将收到一个编译器错误,指出找不到该类型。

快速解决此问题的方法是在 tsconfig.json 选项中包含 cypress 目录,如下所示:

"include": [
"src",
"cypress"
]

TypeScript 将监视 cypress 文件夹中的所有文件,并获取 cypress/support/component.ts 文件中定义的类型。

或者,您可以将类型定义移动到外部文件,并在 tsconfig.json 中包含该文件。参见我们的 TypeScript 配置 指南获取更多信息。

如何让 TypeScript 识别 Cypress 类型而不是 Jest 类型?

对于默认包含 Jest 的框架(如 Create React App),可能会遇到 Cypress 全局类型与 Jest 全局类型(describetestit 等)冲突的问题。在这种情况下,其他 Cypress 全局变量如 Cypresscy 可能也无法正常工作。

我们正在研究更好的处理方法,但目前建议在受影响的每个 spec 文件中使用 三斜线引用指令 来指示 TypeScript 编译器查看 Cypress 全局类型:

/// <reference types="cypress" />

替代方案:重新定位组件 Spec

您还可以将 Cypress 和 Jest 测试分组到单独的文件夹中(不与组件共存)。

需要在文件夹中添加 tsconfig.json 并指定该文件夹中的文件应使用的类型。

别忘了更新 specPattern 以包含新的文件位置。

目录