本文档解释了 Cypress 中的命令执行系统,这是测试中命令排队和执行的核心机制。它涵盖了命令的创建、入队、执行以及它们如何与被测应用程序 (AUT) 交互。有关网络拦截的信息,请参阅 网络拦截。
Cypress 测试使用链式 API (cy.command()) 编写,但调用时不会立即执行。相反,Cypress 使用命令队列以顺序、异步的方式管理执行。此队列是 Cypress 处理时序、重试命令和提供详细日志能力的基础。
来源: packages/driver/src/cypress/command_queue.ts, packages/driver/src/cypress/cy.ts
命令执行系统包含四个主要组件
来源: packages/driver/src/cypress/cy.ts, packages/driver/src/cypress/command_queue.ts, packages/driver/src/cypress/command.ts, packages/driver/src/cypress/chainer.ts
The $Cy 类是命令执行系统的主要控制器。它提供了创建、排队和执行命令的接口。每个测试都有一个 $Cy 实例,该实例作为全局 cy 对象可用。
主要职责
来源: packages/driver/src/cypress/cy.ts:142-783
The CommandQueue 类管理待执行命令的队列。它通过特定于 Cypress 命令执行的附加功能扩展了一个基础的 Queue 类。
主要职责
来源: packages/driver/src/cypress/command_queue.ts:114-624
The $Command 类代表队列中的单个命令。每个命令都有一组属性和在处理过程中会发生变化的 state。
关键属性
name: 命令的名称(例如,“get”、“click”)args: 传递给命令的参数fn: 要执行的函数type: 命令类型(父命令、子命令、双命令、查询命令)subject: 传递给命令或从命令返回的 subjectprev/next: 指向队列中相邻命令的引用状态
queued: 命令正在等待执行pending: 命令正在执行中passed: 命令成功完成failed: 命令遇到错误skipped: 命令已被跳过来源: packages/driver/src/cypress/command.ts:6-183
The $Chainer class提供了链式 API,该 API 允许在 Cypress 测试中使用流畅的语法。每个链式命令都会创建一个新的 $Chainer 实例。
主要职责
来源: packages/driver/src/cypress/chainer.ts:5-38
Cypress 中的命令执行流程遵循一系列不同的步骤
来源: packages/driver/src/cypress/cy.ts:619-782, packages/driver/src/cypress/command_queue.ts:231-402
当用户编写 cy.get('.button') 时,会发生以下情况:
cy 对象上调用 get 方法$Chainer 实例来源: packages/driver/src/cypress/cy.ts:720-782, packages/driver/src/cypress/command_queue.ts:150-177
一旦命令进入队列,它们就会被逐一执行
来源: packages/driver/src/cypress/command_queue.ts:231-402
每个命令都接收前一个命令的 subject,并可以返回一个新的 subject 给下一个命令。这构成了“subject 链”,使命令能够操作前一个命令的结果。
例如,在 cy.get('.button').click() 中
cy.get('.button') 返回一个 jQuery 元素作为其 subject.click() 接收该元素作为其 subject 并执行点击操作此 subject 链通过 chainerId 属性进行管理,该属性将命令链接在一个特定的链中。
来源: packages/driver/src/cypress/cy.ts:242-244, packages/driver/src/cypress/command_queue.ts:342-386
Cypress 具有不同类型的命令,每种命令都有特定的行为
父命令启动新的链,不需要前一个 subject
cy.get(), cy.visit(), cy.wait()子命令需要前一个命令的 subject
.click(), .type(), .should()双命令可以作为父命令或子命令
cy.contains(), cy.screenshot()Cypress 区分了查询和命令
cy.get())get, find, 和 containscy.click())click, type, 和 select来源: packages/driver/src/cypress/command_queue.ts:72-112, packages/driver/src/cypress/commands.ts:87-122
Cypress 允许用户通过自定义命令扩展其命令 API。这些命令使用 Cypress.Commands.add() 方法进行注册。
在内部,自定义命令遵循与内置命令相同的执行流程。
Commands 模块进行注册。$Chainer.prototype。根据 prevSubject 选项,自定义命令可以是父命令、子命令或双命令。
prevSubject: false (默认): 父命令,不需要前一个 subjectprevSubject: true: 子命令,需要前一个 subjectprevSubject: 'optional': 双命令,可以带或不带前一个 subject 使用还可以通过使用 Cypress.Commands.addQuery() 使自定义命令成为查询。
来源: packages/driver/src/cypress/commands.ts:43-204, packages/driver/cypress/e2e/commands/commands.cy.js:41-156
Cypress 的一个关键特性是自动重试查询和断言,直到它们成功或超时。
此重试机制是在 $Cy 类的 retry 方法中实现的,并适用于
cy.get(), cy.find()).should('exist'), .should('have.text', 'Hello'))来源: packages/driver/src/cypress/cy.ts:259-269
Cypress 在继续执行命令之前会等待应用程序稳定。这确保了在与之交互之前应用程序处于一致的状态。
稳定性系统
来源: packages/driver/src/cy/commands/navigation.ts:170-410
当命令失败时,Cypress 会
错误处理系统旨在为开发人员提供有关问题所在以及发生原因的清晰信息。
来源: packages/driver/src/cypress/cy.ts:364-469, packages/driver/src/cypress/error_messages.ts
与大多数 JavaScript 测试库不同,Cypress 不在其公共 API 中使用 Promise 来处理异步性。相反,它使用命令队列来顺序执行命令。这种方法具有几个优点:
在内部,Cypress 使用 Promise 和 async 函数来实现命令队列。 CommandQueue.run() 方法按顺序处理命令,并使用 Promise 来处理异步性。
来源: packages/driver/src/cypress/command_queue.ts:406-459
命令执行系统是 Cypress 运行的核心。它为 Cypress 特有的测试方法提供了基础,包括自动等待、重试和详细的错误消息。理解这个系统是编写有效的 Cypress 测试和使用自定义命令扩展 Cypress 的关键。
其核心在于系统由:
这种架构在复杂的浏览器环境中,在提供强大的测试执行能力的同时,也带来了 Cypress 用户友好的 API。