Cypress自动化测试框架基础(一)

Cypress教程

第一章、Cypress简介

一、E2E测试框架,一般不涉及接口

  • E2E:端到端是网络连接。网络要通信,必须建立连接,不管有多远,中间有多少机器,都必须在两头(源和目的)间建立连接,一旦连接建立起来,就说已经是端到端连接了,即端到端是逻辑链路,这条路可能经过了很复杂的物理路线,但两端主机不管,只认为是有两端的连接,而且一旦通信完成,这个连接就释放了,物理线路可能又被别的应用用来建立连接了。TCP就是用来建立这种端到端连接的一个具体协议
  • 端到端测试(End-to-End Testing)是一种软件测试方法,旨在验证整个应用程序或系统在实际运行环境中的完整性和正确性。这种测试方法模拟真实的用户场景,从用户的角度来测试应用程序或系统的功能、性能、可靠性和安全性等方面。

    在端到端测试中,测试人员会模拟用户交互,并检查应用程序或系统是否按照预期运行。这种测试方法需要测试整个系统的所有组件和集成,包括前端用户界面、后端服务和数据库等。端到端测试可以在开发过程的早期进行,以帮助发现和解决问题,也可以在软件发布前进行,以确保软件符合用户需求和规格。

    端到端测试可以使用各种自动化测试工具和框架来执行,如Selenium、Appium、TestCafe等。自动化端到端测试可以提高测试效率、减少测试时间和成本,并确保测试的一致性和可重复性。

二、使用JavaScript语言,不同与常用的自动化框架

三、使用JS测试框架mocha(主要做浏览器的操作)&chai(做断言)

四、架构不同与selenium

  • 直接由JS驱动浏览器,执行效率比selenium快

        Cypress是一个完备的测试框架,自带了测试运行器(Test Runner),单元测试框架(Mocha),断言库(Chai-jQuery),唯一需要做的就是编写测试代码,ALL IN ONE,从对比结果中发现Cypress不支持并发运行,但是它提供了跨虚拟机并行运行测试脚本并手机测试结果的能力;脚本语言目前只支持JavaScript或许这也是使用范围不佳的原因之一

五、Mocha测试框架

        Mocha是一个JavaScript测试框架,用于编写自动化测试。它可以在浏览器和Node.js环境中运行,并提供了一套简洁、灵活的API,可以编写各种类型的测试,如单元测试、集成测试和端到端测试。

        Mocha测试框架支持多种风格的测试,如BDD(行为驱动开发)和TDD(测试驱动开发),它使用断言库(如Chai)来编写测试断言。它还提供了一个灵活的钩子函数机制,可以在测试执行过程中执行一些预处理或后处理任务。

        Mocha支持并行测试执行、测试结果输出和错误处理等功能,它也可以集成其他工具和框架,如Webpack、Jenkins和Travis CI等,以提高测试效率和可靠性。Mocha被广泛应用于JavaScript开发中的测试任务,是一个受欢迎的JavaScript测试框架之一。

六、Chai-jQuery 库

        Chai-jQuery是一个基于Chai断言库和jQuery库的JavaScript测试工具,用于编写jQuery代码的自动化测试。它提供了一系列的断言和查询函数,使得测试人员可以更方便地编写jQuery测试用例。

        Chai-jQuery可以轻松地集成到Mocha等JavaScript测试框架中,也可以与其他测试工具和框架一起使用。它可以在浏览器和Node.js环境中运行,并且提供了许多常用的jQuery测试用例,如元素选择器、属性操作、事件处理等等。

        使用Chai-jQuery可以大大简化jQuery代码的测试,同时也可以提高测试代码的可读性和可维护性。它被广泛应用于Web应用程序开发中的自动化测试。

七、Cypress的局限性

  • 不建议使用Cypress用于网站爬虫或性能测试
  • Cypress不支持多标签测试
  • Cypress不支持同时打开两个及以上的浏览器
  • 每个Cypress测试用例应遵守同源策略即协议相同,域名相同,端口相同,否则自动报错
  • 目前只支持Chrome、Firefox、Microsoft Edge和Electron
  • 不支持移动端
  • 对于iframe的支持有限
  • 不能在window.fetch上使用cy.route()
  • 没有影子DOM支持

Cyrepss有免费版本和收费版本,针对于需要进一步提高测试效率更有效的组织分析测试结果,收费版本还有基于持续集成的Dashboard 

 八、Cypress的特点

1、时间旅行(time travel)

  • Cypress在测试代码运行时自动拍摄快照,测试运行结束后用户可在Cypress提供的Test Runner里面,通过将鼠标悬停在命令日志上查看运行时候每一步都发生了哪些事

2、实时加载

  • 当测试代码修改后,Cypress会自动加载变更的代码并重新运行测试

3、运行结果一致性

  • 它不使用Selenium/WebDriver,在运行效率可靠性测试结果一致性上均有良好的保障,解决了selenium多次运行但结果不一致的问题

4、调试功能

  • 不用猜测为什么测试失败,直接从Chrome DevTools(开发者工具)进行调试,高可读性的错误提示和堆栈跟踪,调试更便利

5、自动等待

  • 无须再测试中添加wait或sleep,它会自动等待元素至可操作状态时才执行命令或断言,更有助于异步操作

6、网络控制

  • 它可以Mock服务器返回结果,无须连接后端服务器即可实现控制,模拟网络请求
  • Mock 服务器是一个用于模拟 API 接口行为的服务器。它可以在开发和测试阶段,用来模拟实际的后端 API 接口,从而帮助前端开发人员在没有实际后端 API 支持的情况下进行开发和测试。通过使用 Mock 服务器,开发人员可以更快速地进行开发和测试,并且可以更容易地调试和修复问题。

    Mock 服务器通常可以模拟 HTTP 请求和响应,并提供类似于真实 API 接口的数据返回,包括 JSON 数据、XML 数据、图像、视频等。在开发和测试阶段,开发人员可以使用 Mock 服务器来模拟各种场景,例如模拟接口超时、模拟接口返回错误、模拟接口返回不同数据等等,以确保应用程序在各种情况下都能正常工作。

7、截图和录屏

  • 在测试运行失败时自动截图,在无头运行时录制整个测试套件的过程

第二章、Cypress原理

        大多数测试工具(Selenium/WebDriver)通过在浏览器外部运行并在网络上执行远程命令来运行(WebDriver底层的通信协议基于JSON Wire Protocol其运行需要网络通信),Cypress恰恰相反,它在与应用程序相同的生命周期里执行,当运行测试时,Cypress首先使用webpack将测试代码中的所有模块bundle到一个js文件中,然后,它会运行浏览器并将测试代码注入一个空白页面里,然后它将在浏览器中运行测试代码(可以理解为Cypress通过一系列操作将测试代码放到一个iframe中运行)

        在每次测试首次加载Cypress时,内部Cypress Web应用程序先把自己托管在本地的一个随机端口上(例如http://localhost:65874/__/),在识别出测试中发出的第一个cy.visit()命令后,Cypress将会更改其本地URL以匹配你远程应用程序的Origin(用于满足同源策略),这使得你的测试代码和应用程序可以在同一个Run Loop中运行

        因为Cypress测试代码和应用程序均运行在由Cypress全权控制的浏览器中,且他们运行在同一个Domain下的不同iframe内,所以Cypress的测试代码可以直接操作DOM、Window Objects甚至Local Storages而无须通过网络访问,也因此它更快

        此外Cypress还可以在网络(请求)层进行及时读取和更改网络流量的操作,Cypress背后是NodeJS Process,任何发往浏览器之外的HTTP的请求和响应,均由Cypress生成,被NodeJS Process控制的Proxy进行转发,因此Cypress不仅可以修改进出浏览器的所有内容,还可以更改可能影响自动化浏览器操作的代码,所以Cypress不仅从根本上控制整个自动化测试的流程,还可以提供稳定性更佳的结果

        在Cypress测试代码中,代码运行在Node.js环境中,而浏览器环境是由Cypress框架模拟和管理的。当我们在测试代码中访问浏览器环境的对象和API时,Cypress会将其转换为对应的命令,并发送到浏览器环境中执行。例如,当我们调用cy.get('button')方法时,Cypress会将其转换为在浏览器环境中执行的查找元素的命令,并返回结果给测试代码。这种方式可以让我们在测试代码中方便地操作和验证浏览器行为,而无需手动打开浏览器进行测试。

一、Node.js环境

        Node.js是一个基于Chrome V8引擎的JavaScript运行环境,可以使JavaScript脱离浏览器运行在服务器端。在Node.js环境中,可以使用JavaScript编写各种类型的应用程序,例如Web应用程序、命令行工具、桌面应用程序等。Node.js提供了许多内置的模块和API,例如文件系统、网络通信、进程管理、数据库访问等,方便开发者进行各种操作。

1、Node.js环境支持的模块导入语法

        Node.js环境中,可以使用CommonJS模块和ES6的动态导入(即使用import()语法)。

        在Node.js 14及以上版本中,也可以使用ES6的静态导入(即使用import语法)。但是,在使用ES6的静态导入时,需要设置"type": "module"选项,告诉Node.js该文件是ES6模块。同时,还需要注意,ES6的静态导入目前还不支持文件的相对路径,只能使用绝对路径。

        总之,Node.js环境对于模块的支持情况是随着版本的升级而不断变化的,可以根据自己的需求和实际情况选择合适的模块化方案。

二、浏览器环境

        浏览器环境是指JavaScript在Web浏览器中运行的环境。浏览器环境和Node.js环境的主要区别在于,浏览器环境中可以访问DOM和BOM对象,例如window、document、location等,而Node.js环境中不能访问这些对象。另外,浏览器环境中也提供了许多Web API和事件机制,例如XMLHttpRequest、WebSocket、Canvas、事件处理器等,可以方便地操作Web页面和响应用户交互。

三、运行在Node.js环境中的文件

运行在 Node.js 环境中:

  • cypress.config.ts - Cypress 的配置文件
  • cypress/plugins/index.js - Cypress 插件配置文件
  • cypress/support/index.js - 测试辅助函数和自定义命令
  • cypress/plugins/index.ts - TypeScript 版本的插件配置文件
  • cypress/support/index.ts - TypeScript 版本的测试辅助函数和自定义命令

四、运行在浏览器环境中的文件

  • cypress/integration/*.spec.js - 测试用例文件
  • cypress/integration/*.spec.ts - TypeScript 版本的测试用例文件
  • cypress/fixtures/* - 测试用到的静态文件,例如 JSON 或者图片
  • cypress/pages/* - 页面对象模型(Page Object Model)文件,包含与页面相关的元素和行为
  • cypress/commands.js - 自定义命令的实现代码

        值得注意的是,Cypress 中的一些文件(例如测试用例文件)可以同时运行在 Node.js 和浏览器环境中,这是因为 Cypress 中使用了特殊的测试框架和编写 API,可以使得测试文件同时兼具运行在两个环境中的能力。

五、加载文件顺序

Cypress 测试框架在运行前会按照以下顺序加载文件:

  1. cypress.config.ts文件 Cypress 测试框架会首先加载项目根目录下的 cypress.config.ts文件,该文件是 Cypress 测试框架的全局配置文件,用于设置 Cypress 的全局配置。

  2. cypress/plugins/index.js 文件 Cypress 测试框架会加载 cypress/plugins/index.js 文件,该文件是 Cypress 测试框架的插件文件,用于进行一些插件的设置和配置。

  3. cypress/support/index.js 文件 Cypress 测试框架会加载 cypress/support/index.js 文件,该文件是 Cypress 测试框架的支持文件,用于进行一些全局的设置和配置。

  4. cypress/integration 目录下的测试文件 Cypress 测试框架会加载 cypress/integration 目录下的测试文件,该目录是存放测试用例的目录,用于编写和执行测试用例。

        需要注意的是,Cypress 测试框架的文件加载顺序是固定的,并且在加载文件时会自动执行相应的代码。因此,如果需要在文件加载时进行一些初始化操作,可以将代码放在相应文件的顶部,以确保在文件加载时自动执行。

六、Cypress架构图

        WebSocket 是一种用于在客户端和服务器之间进行双向通信的网络技术。它允许在单个 TCP 连接上进行持久连接,从而使得服务器可以实时向客户端推送数据,而不需要客户端轮询请求。WebSocket 还提供了一种全双工(双向)通信的机制,这意味着客户端和服务器可以同时发送和接收数据。

        与传统的 HTTP 请求-响应模型不同,WebSocket 连接建立后,客户端和服务器之间可以发送任意数量的消息,而无需为每个消息都创建一个新的 HTTP 请求。WebSocket 还支持跨域通信,这使得开发人员可以从任何地方访问服务器,并进行实时通信,而无需担心跨域限制。

        WebSocket 技术已被广泛用于各种实时应用程序,例如在线聊天、多人游戏、股票行情、运动比赛等。

七、Cypress的架构包含四个核心组件

1、Cypress Test Runner

Cypress Test Runner:是Cypress的用户界面(UI)部分,用于运行和查看测试结果。

2、Cypress Automation Backend

        Cypress Automation Backend:是Cypress的后台部分,它控制着浏览器,执行测试用例并将结果返回给Test Runner。Automation Backend使用Chrome DevTools Protocol(开发者工具协议)与浏览器进行通信。

3、Browser

        Browser:是Cypress测试过程中的目标应用程序,Automation Backend直接嵌入浏览器,通过使用DevTools协议来控制浏览器执行测试用例。

4、Cypress Application Under Test

        Cypress Application Under Test:是Cypress测试用例运行的目标应用程序,也是Automation Backend控制的应用程序。

        Cypress的测试用例是由JavaScript编写的,它们通过Cypress的API来控制浏览器并与目标应用程序交互。测试用例由Test Runner直接运行,并由Automation Backend控制浏览器执行测试用例,并将结果返回给Test Runner。

        通过这种架构,Cypress实现了与浏览器的直接通信和使用JavaScript控制测试过程,提供了一种快速、可靠、易于学习和使用的前端自动化测试解决方案。

八、测试运行结果对象(test run result object)

        Cypress 框架会在测试运行期间,将所有测试用例的运行结果(包括每个测试用例的状态、通过/失败的数量、测试用例的执行时间、错误信息等)记录下来,并将其存储在一个对象中。这个对象可以在测试运行结束后用于分析测试结果,帮助开发者更好地了解测试运行的情况,定位问题并进行改进。这个对象通常被称为测试运行结果对象(test run result object),也可以简称为结果对象(result object)或统计对象(statistics object)。

第三章、基础环境安装

一、安装nodejs

Node.js官网官网下载node.js安装包

  • node.js是谷歌开发的,基于Chrome V8引擎,可以在浏览器外部执行JavaScript代码

 下载完成后,双击安装,在Custom Setup阶段,注意确保添加系统环境变量的选项(Add to PATH)是选中的否则后续还需要自行配制

1、环境验证

C:\Users\13476>node -v
v18.12.1

C:\Users\13476>npm -v
8.19.2

C:\Users\13476>node
Welcome to Node.js v18.12.1.
Type ".help" for more information.
>
(To exit, press Ctrl+C again or Ctrl+D or type .exit)
>

C:\Users\13476>

二、Cypress安装与配置

1、创建padkage.json文件

  • 新建一个Cypress的文件夹,然后在文件夹内执行命令npm init或npm init -y命令从而生成package.json文件,主要是为了生成依赖包

C:\Users\13476\Desktop\Cypress>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (cypress)
version: (1.0.0)
description: first
entry point: (index.js)
test command:
git repository:
keywords:
author: jzq
license: (ISC)
About to write to C:\Users\13476\Desktop\Cypress\package.json:

{
  "name": "cypress",
  "version": "1.0.0",
  "description": "first",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "jzq",
  "license": "ISC"
}


Is this OK? (yes)

C:\Users\13476\Desktop\Cypress>

双击该*.json文件可在vscode中打开

2、安装Cypress

在刚才创建json文件所在目录中

2.1、国外安装

直接使用npm install cypress --savenpm install cypress --save-dev(开发模式)命令安装Cypress

2.2、国内安装

首先安装cnpm,使用命令npm install -g cnpm --registry=https://registry.npm.taobao.org

然后执行cnpm install cypress@5.6.0 --save-dev命令  (如不指定版本,则默认最新)

cypress版本介绍

目前最新版本的 Cypress 是 12.9.0,它的前置要求是 Node.js 版本 12 或更高版本。在这之前的一些主要版本包括:

  • Cypress 8.x:在 8.x 版本中,Cypress 引入了许多新功能和改进,包括支持 Firefox 浏览器、异步命令超时、自定义命令别名等。
  • Cypress 7.x:在 7.x 版本中,Cypress 支持了许多新的浏览器特性,包括原生的 Web Components 和 Shadow DOM 支持、跨域测试等。
  • Cypress 6.x:在 6.x 版本中,Cypress 引入了新的测试运行器,提供了更好的性能和可靠性,并支持了更多的自定义配置选项。
  • Cypress 5.x:在 5.x 版本中,Cypress 支持了许多新的特性,包括访问 HTTP 请求和响应、支持定位多个元素、支持组合自定义命令等。

查看cypress版本命令:cypress version

因为cypress不是全局安装的,所以只能进入安装文件夹中打开DOS窗口输入命令查看

执行命令报错两种情况:

1、安装完成后要重开DOS窗口

2、未配置环境变量

        Cypress 的安装路径取决于您的操作系统和 Cypress 的安装方式。如果您使用 npm 或 yarn 安装 Cypress,其默认安装路径为 node_modules/.bin 目录。因此,您需要将该目录添加到 PATH 环境变量中。如果您使用其他方式安装 Cypress,请参考其安装文档以确定其安装路径

安装完cypress后会生成依赖包

3、打开cypress

查看命令

.\node_modules\.bin\cypress --help

打开cypress 

3.1、方法一:

.\node_modules\.bin\cypress open

3.2、方法二:

 在终端执行命令

npm run Cypress

3.3、方法三:

 在终端执行命令

npx cypress open

选择测试类型

选择浏览器类型

选择测试case执行

 执行结果及其对应显示

3.3、启动报错

1、确保您的 package.json 文件中 Cypress 的版本号正确

package.json 中找到 Cypress 的版本号,然后执行以下命令更新 Cypress:

npm install cypress@<version> --save-dev

报错一:

 

4、npx介绍

npx详解

   npx (Node Package Runner)和 npm(Node Package Manager) 是两个不同的 Node.js 工具,它们之间有着一些区别和联系。

  1. npm 是 Node.js 的包管理器,用于安装、升级和删除 Node.js 模块。它还提供了一些有用的命令,如 npm initnpm installnpm update 等。与此不同,npx 仅用于执行已安装的模块或从 npm 仓库下载和执行模块

  2. npm 可以在全局范围内安装模块,使其在任何目录中可用。与此相比,npx 不需要全局安装模块,它会在本地环境中查找并执行命令,从而避免了全局污染和版本冲突的问题。

  3. npm 默认会将模块安装到本地 node_modules 目录中,并在项目中引用它们。npx 则不需要将模块安装到本地目录中,而是直接在本地运行模块,从而避免了手动安装和维护模块版本的麻烦。

  4. npx 可以运行 npm 仓库中的最新版本的模块,并支持从 GitHub 存储库中运行脚本和命令。这使得 npx 成为快速测试、试用和共享代码的有用工具。

        综上所述,npxnpm 是两个不同的工具,它们在安装、管理和执行 Node.js 模块时有不同的功能和用途。虽然它们有一些相似之处,但它们的目标和设计思路不同。

三、IDE VScode配置

1、界面介绍

2、vscode编辑器Cypress代码提示

方法一:

在每个cy.js文件头添加

/// <reference types="Cypress" />

方法二:

在项目根目录下创建jsconfig.json文件

//jsconfig.json
{
    "include": [
        "/node_modules/cypress",
        "cypress/**/*.js"
    ]
}

方法三:

安装插件

 安装完成后重启vscode

3、vscode打开多个标签

vscode怎么双开窗口 - VSCode教程 - 站长源码网(Downzz.com)

vscode设置打开多个标签页_vscode设置打开多个页面_Aaron莫言的博客-CSDN博客

必须要在打开设置的前提下

4、vscode常用插件安装

  • 安装jsdoc插件

具体操作如下:

  1. 在 VS Code 中安装 vscode-jsdoc 插件。可以在扩展商店中搜索 jsdoc 并安装该插件。

  2. 在 JavaScript 文件中编写函数或方法时,在函数或方法的上方输入 /** 并按下回车键,VS Code 将自动生成一个 JSDoc 注释模板。例如:

**
 * Add two numbers.
 * @param {number} a - The first number to add.
 * @param {number} b - The second number to add.
 * @returns {number} The sum of a and b.
 */
function add(a, b) {
  return a + b;
}

        需要注意的是,生成的 JSDoc 注释模板并不是完整的,需要根据实际需要添加必要的标记和描述文本。在使用 JSDoc 注释时,建议参考官方文档和示例,以便正确使用 JSDoc 注释和获得更好的文档和类型检查效果。

        JSDoc 注释是一种特殊的注释格式,用于为 JavaScript 代码添加文档和类型注释。它的语法与常规注释类似,但以 /** 开始,以 */ 结束,并在开头添加一个或多个 @ 标记,用于指定注释的类型和参数。

4、运行机制

        在VScode上编辑文件后可直接同步到Cypress

        当测试代码修改后,Cypress会自动加载变更的代码并重新运行测试

第四章、测试框架(mocha)

自动化测试框架[Cypress测试用例]_cypree测试案例_Davieyang.D.Y的博客-CSDN博客

        Mocha 是一个基于 Node.js 平台的测试框架,旨在提供丰富的测试功能,支持异步和同步测试,并且可以运行在浏览器和命令行环境下。

        Mocha 是一个功能丰富的测试框架,提供了异步和同步测试、多种断言库、测试覆盖率统计、前端测试、多种测试报告、插件、钩子函数、参数化测试等特性,适用于 Node.js 和浏览器环境下的测试。

一、安装mocha

终端执行命令

国内使用:cnpm isntall mocha chai

国外使用:npm isntall mocha chai

二、特性

Mocha 框架提供了以下特性:

  • 异步和同步测试:可以测试异步和同步代码的运行情况;
  • 支持多种断言库:可以使用 Node.js 自带的 assert 库,也可以使用第三方库如 should.js、expect.js、chai.js 等;
  • 支持测试覆盖率:可以通过 Istanbul 等工具进行测试覆盖率的统计;
  • 支持前端测试:可以使用 jsdom 等工具模拟浏览器环境;
  • 支持多种测试报告:可以生成多种格式的测试报告,如 TAP、JSON、XML、HTML 等;
  • 支持插件:可以通过插件扩展 Mocha 的功能;
  • 支持钩子函数:可以在测试前、测试后等不同阶段执行一些操作;
  • 支持参数化测试:可以使用第三方库如 mocha-param 等进行参数化测试。

三、使用

使用 Mocha 进行测试的基本流程如下:

  1. 在测试文件中引入 Mocha 和断言库;
  2. 使用 describe 函数定义一个测试套件,可以包含多个测试用例;
  3. 使用 it 函数定义一个测试用例,可以使用多个断言判断测试结果是否正确;
  4. 使用 assert 断言库或其他第三方断言库进行测试;
  5. 在命令行中执行测试文件,Mocha 会自动执行测试用例并输出测试结果。
const assert = require('assert');
const add = require('./add');

describe('Add function', function() {
  it('should add two numbers', function() {
    assert.equal(add(2, 3), 5);
  });

  it('should return NaN when a non-number is used', function() {
    assert.ok(isNaN(add(2, 'test')));
  });
});

四、命令行接口

Mocha 提供了命令行接口,可以在命令行中运行测试用例。

运行所有测试:

mocha

运行指定文件或目录下的测试:

mocha test.js

mocha test/

        支持多种命令行参数,如 -R 指定测试报告格式,-t指定超时时间,--recursive 递归执行子目录中的测试等等。

五、钩子函数

Mocha 提供了一些钩子函数,在测试前、测试后等不同阶段执行一些操作,例如:

  • before:在测试套件中所有测试用例执行前执行;
  • after:在测试套件中所有测试用例执行后执行;
  • beforeEach:在每个测试用例执行前执行;
  • afterEach:在每个测试用例执行后执行。

示例代码:

describe('Array', function() {
  let arr = [];

  before(function() {
    // 在所有测试用例执行前执行
    arr = [1, 2, 3];
  });

  after(function() {
    // 在所有测试用例执行后执行
    arr = [];
  });

  beforeEach(function() {
    // 在每个测试用例执行前执行
    console.log('beforeEach');
  });

  afterEach(function() {
    // 在每个测试用例执行后执行
    console.log('afterEach');
  });

  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal(arr.indexOf(4), -1);
    });
  });
});

六、参数化测试

        参数化测试可以用来避免大量重复的测试代码。Mocha 本身不支持参数化测试,但可以使用第三方库如 mocha-param 进行参数化测试。

示例代码:

const param = require('mocha-param');
const add = require('./add');

describe('Add function', function() {
  param([
    { args: [2, 3], expected: 5 },
    { args: [0, 0], expected: 0 },
    { args: [-1, 1], expected: 0 },
  ], function(testCase) {
    it(`should add ${testCase.args} to get ${testCase.expected}`, function() {
      const result = add(testCase.args[0], testCase.args[1]);
      assert.equal(result, testCase.expected);
    });
  });
});

七、describe 和 it关键字详细介绍

在Mocha测试框架中,describeit是两个最常用的关键字。

1、describe用于对一个测试套件进行描述和组织,通常包含一组相关的测试用例。它接受两个参数:一个字符串描述和一个函数。函数中包含一个或多个测试用例,每个测试用例都是由it关键字定义的。

2、it函数用于定义一个测试用例,它包含两个参数:一个字符串描述和一个函数。函数中包含我们要测试的代码和断言。

1、嵌套使用

describeit可以嵌套使用,以进一步组织测试用例。例如:

describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal([1,2,3].indexOf(4), -1);
    });
    it('should return the index when the value is present', function() {
      assert.equal([1,2,3].indexOf(2), 1);
    });
  });
});

        在这个示例中,我们在describe('#indexOf()'中定义了两个测试用例,一个测试用例描述为当值不存在时,indexOf()应该返回-1,另一个测试用例描述为当值存在时,indexOf()应该返回对应的索引。

2、异步测试

        在Mocha中,测试用例可以是异步的。我们可以使用done参数告诉Mocha等待异步操作完成后再继续执行测试。例如:

describe('Async test', function() {
  it('should execute asynchronously', function(done) {
    setTimeout(function() {
      assert.ok(true);
      done();
    }, 100);
  });
});

        在这个示例中,我们使用setTimeout模拟异步操作,并在回调函数中调用done参数。这告诉Mocha等待异步操作完成后再继续执行测试。

3、超时时间

        如果测试用例执行时间过长,Mocha会认为测试用例失败。我们可以使用this.timeout()方法来设置测试用例的超时时间。例如:

describe('Long running test', function() {
  this.timeout(10000);
  it('should execute within 10 seconds', function(done) {
    setTimeout(function() {
      assert.ok(true);
      done();
    }, 5000);
  });
});

        在这个示例中,我们使用this.timeout(10000)设置测试用例的超时时间为10秒。在测试用例中,我们使用setTimeout模拟长时间的操作,但仍然在超时时间内完成操作。

八、Cypress和Mocha的关系

        Cypress和Mocha是两种不同的JavaScript测试框架,它们有一些相似之处,但是也有一些不同之处。

        Mocha是一个非常流行的JavaScript测试框架,它可以用于编写各种类型的测试,包括单元测试、集成测试和端到端测试等。Mocha提供了一个灵活的测试运行时,并且可以与各种断言库和测试报告器集成。

        Cypress是一个用于Web应用程序测试的端到端测试框架。Cypress可以在浏览器中模拟用户与应用程序的交互,并可以对应用程序的各个方面进行测试,例如用户界面、网络请求、安全性等等。Cypress还提供了实时重新加载和调试功能,可以帮助开发人员更快地调试和修复测试用例。

1、虽然Cypress和Mocha是两个不同的测试框架,但是它们可以很好地结合使用。Cypress使用Mocha作为其测试运行时,并且支持使用Mocha的各种插件和扩展。此外,Cypress还提供了许多自己的API和功能,以便更轻松地编写端到端测试。

2、Cypress直接集成了Mocha,Mocha是Cypress的默认底层框架

3、使用Mocha框架不需要显示导入,使用其关键字就相当于使用了该框架

第五章、断言(chai)使用

 二、非Cypress框架需导入chai

需要导包

var should = require('chai').should();

三、Cypress框架不需要导入chai

四、案例

test02.js
var should = require('chai').should();

describe('test a variable',()=>{
    let a = 'jiazhuoqun';
    it('a should be a string',()=>{
        a.should.be.a('string');
    })
    it('a should equal to jiazhuoqun', () => {
        a.should.equal('jiazhuoqun');
    });
})

终端执行命令

npx mocha .\test02.js

第六章、Cypress全局对象

一、Cypress全局对象介绍

        在 Cypress 框架中,Cypress 对象是一个全局对象,用于管理整个测试运行时的配置和状态。

二、Cypress全局对象默认位置

        Cypress 全局对象默认在浏览器环境中,而不是 Node.js 环境中,虽然 Cypress 全局对象默认在浏览器环境中,但是 Cypress 测试框架本身是基于 Node.js 开发的,并且可以使用一些 Node.js 模块和 API,例如 fspathhttp 等等。因此,如果需要在测试中使用 Node.js 相关的功能,可以在测试文件中使用 Node.js 模块和 API 来实现。

Cypress是一个基于 Electron 构建的桌面应用程序,它同时也包含了一个 Node.js 的运行时环境和一个 Chromium 的浏览器环境因此,虽然Cypress 全局对象默认在浏览器环境中,但是Cypress也可以在 Node.js 和浏览器环境中都使用,并提供了不同的 API 来访问这两个环境中的对象和方法。在测试脚本中,Cypress对象和cy对象都是在浏览器环境中使用的,而在配置文件中和插件文件中使用的Cypress对象则是在 Node.js 环境中使用的。

1、默认在测试用例文件中

        因为测试文件是编写测试用例的主要文件,运行在浏览器环境中,其中可以使用 Cypress 对象来管理测试运行时的配置和状态。在测试文件中,可以使用 Cypress 对象的方法和属性来进行配置和使用。

2、默认在支持文件中

cypress/support/index.js 自定义命令的实现代码

3、默认在配置文件中

cypress.config.ts文件中

4、默认在插件文件中

cypress/plugins/index.js

        这四个文件是 Cypress 的核心文件,Cypress 在启动测试之前会自动加载它们,所以它们默认就有 Cypress 对象。

        对于其他的文件,如果想要使用 Cypress 对象,需要手动导入。这是因为 Cypress 运行测试的方式与普通的 JavaScript 文件不同,Cypress 是通过特殊的方式运行测试的,所以需要手动导入 Cypress 对象。

三、其他文件位置需手动导入

如果在其他文件中需要使用Cypresscy对象,需要手动导入。

要在其他文件中使用Cypress对象和cy对象,需要在文件顶部导入它们。在Node.js中,可以使用CommonJS模块化规范的语法进行导入:

const Cypress = require('cypress')
const cy = Cypress.cy

其中,require('cypress')会返回一个对象,即Cypress对象,通过Cypress.cy可以访问到cy对象。在ES6中,可以使用import语法进行导入:

import { cy } from 'cypress'

        这样就可以在文件中使用cy对象了。需要注意的是,除了测试文件之外,其他文件中使用cy对象的情况应该尽量避免,因为这可能会导致测试代码和其他代码的耦合度增加,不利于代码的维护。

1、ReferenceError: Cypress is not defined

Cypress环境:就是指在e2e目录下创建 .cy.ts文件,采用Mocha框架模式编写的代码

cypress环境中自然会有Cypress和cy对象,无需导入

        如果你在非Cypress环境中运行了Cypress的代码,例如在Node.js环境中直接运行测试代码,就会遇到ReferenceError: Cypress is not defined错误。这是因为Cypress的API只能在Cypress的运行环境中使用。如果需要在非Cypress环境中测试代码,可以考虑使用其他测试框架。

        它包含了 Cypress 框架提供的全局函数和属性,下面是 Cypress 对象的一些常用属性和方法的详细介绍:

四、Cypress对象常用属性

1、Cypress.version

Cypress.version属性,返回 Cypress 框架的版本号。

2、Cypress.Commands

  Cypress.Commands属性,注册自定义命令,扩展 Cypress 的测试命令库。可以在 support/commands.js 文件中定义自己的命令,或者使用 Cypress.Commands.add() 方法动态注册命令。

3、Cypress.Cookies

  Cypress.Cookies属性,管理浏览器 cookie 的 API。可以使用 Cypress.Cookies.set()Cypress.Cookies.clear() 等方法来操作浏览器 cookie。

五、Cypress对象常用方法

1、Cypress.env()

  Cypress.env():方法,获取或设置 Cypress 运行时的环境变量。可以在命令行中使用 CYPRESS_ 前缀来设置环境变量,也可以在配置文件中设置。

在 Cypress 测试代码中,可以通过调用 Cypress.env() 方法来获取环境变量中的某个具体属性。

例如,如果你想获取名为 myVariable 的环境变量属性,可以这样写:

const myVariable = Cypress.env('myVariable')

这将返回 myVariable 属性的值。如果该属性不存在,则返回 undefined

你也可以使用 ES6 解构赋值语法来获取多个环境变量属性:

const { myVariable1, myVariable2 } = Cypress.env()

 这将返回一个包含 myVariable1myVariable2 属性的对象,其中属性值为对应的环境变量值。如果这些属性在环境变量中不存在,则它们的值将为 undefined

2、Cypress.config()

  Cypress.config():方法,获取或设置 Cypress 的配置信息。可以在配置文件中设置各种选项,例如测试文件路径、浏览器类型、测试超时时间等。

        在Cypress测试代码中,如果直接使用console.log(config)输出配置对象,可能会导致无法正确获取到配置对象的值。这是因为,Cypress测试代码默认运行在浏览器环境中,而配置对象是在Node.js环境中加载和初始化的,需要先确保Cypress测试代码和浏览器环境之间建立了正确的通信和同步。

        为了解决这个问题,Cypress提供了Cypress.config()方法,可以在测试代码中安全地访问和修改配置对象的值。Cypress.config()方法是Cypress框架提供的一个全局方法,它可以用来获取和设置Cypress的配置项。在Cypress测试代码中,可以通过Cypress.config()方法来输出配置对象的值,

3、Cypress.on()

  Cypress.on()方法,监听 Cypress 运行时的事件。可以监听各种事件,例如测试运行开始、测试运行结束、命令执行前后等。

4、Cypress.off()

Cypress.off()方法,取消监听 Cypress 运行时的事件。

5、Cypress.log()

  Cypress.log()方法,记录日志信息,可以在测试运行时查看日志信息,例如测试命令执行结果、浏览器错误信息等。

6、Cypress.moment()

Cypress.moment()方法,获取当前时间,使用 Moment.js 库实现。

7、Cypress._

Cypress._:Lodash 库的实例对象,提供了一些常用的函数和工具,例如数组操作、对象操作、字符串操作等。

        总之,Cypress 对象是 Cypress 框架中非常重要的全局对象,它提供了许多常用的函数和属性,用于管理测试运行时的配置和状态,扩展测试命令库,记录日志信息等。在编写测试脚本时,应充分利用 Cypress 对象提供的功能,编写出具有高可读性和可维护性的测试脚本。

第七章、cy对象

一、cy对象介绍

        在 Cypress 框架中,cy 对象是一个测试命令库,提供了各种测试命令用于模拟用户在浏览器中的交互操作。cy 对象主要用于编写测试脚本,它包含了一系列测试命令,例如访问页面、输入文本、点击按钮、验证页面元素等。

二、cy对象默认位置

  cy 对象是 Cypress 的核心对象之一,它默认在浏览器环境中运行,用于控制和管理测试中的交互和行为

1、默认在测试用例文件中

在测试文件中,cy 对象是全局可访问的,因此可以在测试文件的任何位置使用,无需手动导入。

2、默认在支持文件中

 cy 对象还可以在自定义命令文件(cypress/support/commands.js)中使用。在这个文件中,你可以自定义一些常用的命令,以便在测试文件中重复使用。在自定义命令文件中,你也不需要手动导入 cy 对象,它会自动注入到命令中,因此可以直接使用。

三、其他文件位置需手动导入

  cy 对象默认只在测试文件和自定义命令文件中可用,而在其他文件中则不会自动注入到作用域中。这是因为 Cypress 设计为一个测试框架,而测试文件和自定义命令文件是 Cypress 的核心组成部分,它们提供了测试脚本和自定义命令的编写方式,因此 cy 对象被设计为在这些文件中全局可用。

        在其他文件中,例如配置文件和插件文件,通常只需要定义一些配置选项、任务或钩子函数,而不需要直接控制或访问测试环境。因此,在这些文件中,cy 对象不会自动注入到作用域中,也不建议手动导入。如果你需要在这些文件中执行某些操作,可以考虑使用 Node.js 模块或者其他第三方库来实现。

四、cy对象常用属性

1、cy.document

        返回当前页面的文档对象。

2、cy.server

       用于模拟后端服务器,可以定义路由和响应。

3、cy.window

返回当前页面的window对象。

4、cy.route

        用于定义和控制XHR请求的路由。

5、cy.version

        一个只读的字符串,表示 Cypress 的版本号。你可以使用 cy.version 来获取当前安装的 Cypress 版本号。

五、cy对象常用方法

下面是 cy 对象的一些常用方法的详细介绍:

  1. cy.visit():访问指定的页面。

  2. cy.get():获取页面上的元素。

  3. cy.contains():查找包含指定文本的元素。

  4. cy.type():模拟用户输入文本。

  5. cy.click():模拟用户点击页面元素。

  6. cy.check():模拟用户勾选或取消勾选复选框或单选框。

  7. cy.select():模拟用户选择下拉框中的选项。

  8. cy.wait():等待指定的时间或者等待指定的条件满足。

  9. cy.reload():刷新当前页面。

  10. cy.url():获取当前页面的 URL。

  11. cy.title():获取当前页面的标题。

  12. cy.contains():查找包含指定文本的元素。

  13. cy.clearCookie():清除指定的浏览器 cookie。

  14. cy.clearLocalStorage():清除浏览器的本地存储。

        总之,cy 对象是 Cypress 框架中非常重要的测试命令库,它提供了各种测试命令用于模拟用户在浏览器中的交互操作。在编写测试脚本时,应充分利用 cy 对象提供的功能,编写出具有高可读性和可维护性的测试脚本。

六、Cypress对象和cy对象的区别和联系

        在 Cypress 框架中,Cypress 对象和 cy 对象都是 Cypress 提供的 API 对象,用于编写前端端到端测试脚本。

它们的区别在于:

  1. Cypress 对象是全局对象,用于管理整个测试运行时的配置和状态。它包含了 Cypress 框架提供的全局函数和属性,例如 Cypress.envCypress.config 等。

  2. cy 对象是测试运行时的命令对象,用于编写测试命令。它包含了 Cypress 提供的一系列测试命令,例如 cy.visit()cy.get() 等。在测试脚本中,可以通过 cy 对象来访问 Cypress 提供的测试命令,执行各种测试操作。

联系:

        在 Cypress 中,Cypress 对象和 cy 对象是紧密联系的。Cypress 对象提供了全局的配置和状态信息,而 cy 对象则用于编写具体的测试命令。在测试脚本中,可以使用 Cypress 对象来访问全局配置和状态信息,也可以通过 cy 对象来访问测试命令,执行各种测试操作。

        总之,Cypress 对象和 cy 对象都是 Cypress 框架中重要的 API 对象,用于编写前端端到端测试脚本。在测试脚本中,应根据需要使用不同的对象来访问全局状态信息和测试命令,编写出具有高可读性和可维护性的测试脚本。

第八章、Cypress config 配置对象

一、config 配置对象介绍

config 对象:这是Cypress的配置对象,包含了Cypress框架的一些配置信息,如测试服务器地址、超时时间、浏览器类型等等。可以通过Cypress.config()方法访问和修改config对象的值,也可以在Cypress项目的cypress.config.ts配置文件中设置config对象的默认值。

        Cypress 的配置对象是一个包含了 Cypress 运行时的各种配置选项的 JavaScript 对象。它定义了 Cypress 的行为方式和功能,例如测试文件的位置、浏览器的类型、超时时间等等。Cypress 的配置对象可以通过 Cypress 的配置文件或命令行选项进行设置。

        Cypress 的配置文件是cypress.config.ts 文件,包含了 Cypress 运行时的各种配置选项,例如测试文件的位置、浏览器的类型、超时时间等等。而配置对象是在 Cypress 启动时根据配置文件或命令行选项生成的一个 JavaScript 对象,包含了 Cypress 运行时的实际配置选项。

        具体来说,当 Cypress 启动时,它会首先加载默认配置选项,然后检查是否存在配置文件或命令行选项来覆盖默认配置。如果存在配置文件,则 Cypress 将读取该文件,并根据文件内容生成配置对象。配置文件中的配置选项会被映射到配置对象的相应属性中。如果不存在配置文件,则配置对象将只包含默认配置选项。如果指定了命令行选项,则 Cypress 将在运行时覆盖默认配置。   

        总之,配置文件是配置对象的来源之一,Cypress 在启动时根据配置文件或命令行选项生成配置对象。配置对象是 Cypress 运行时的实际配置选项,它决定了 Cypress 的行为方式和功能。

二、config配置对象默认位置

   config配置对象是在Cypress的Node.js进程中初始化的。当您启动Cypress测试时,它将启动一个Node.js进程来运行测试,该进程将加载和处理config配置对象。

        在Cypress测试中,Node.js进程负责处理测试文件之外的所有事情,例如加载配置文件、启动浏览器、管理测试进程等。因此,config配置对象是在Node.js进程中初始化和处理的。

        需要注意的是,尽管config配置对象是在Node.js进程中初始化的,但是测试文件是在浏览器环境中执行的。这意味着您不能在测试文件中直接访问Node.js特定的功能和API,例如fs模块和process对象。

        在Cypress中,config配置对象默认是在Node.js环境中而不是在浏览器环境中config对象是Cypress配置文件中的一个JavaScript对象,其中包含了各种配置选项,如测试的根目录、测试运行的浏览器、测试的超时时间等等。这些配置选项在运行测试之前被加载,并在整个测试过程中保持不变。

        由于config对象是在Node.js环境中加载和处理的,因此您可以在配置文件中使用Node.js中的任何可用API和模块,例如fs模块来读取文件、process对象来访问环境变量等等。但是需要注意的是,这些Node.js特定的功能只能在配置文件中使用,而无法在测试文件中使用,因为测试文件是在浏览器环境中执行的。

1、默认在配置文件中

        在Cypress中,config配置对象的默认值通常在cypress.config.ts文件中定义。cypress.config.ts是Cypress的默认配置文件,如果您没有在项目根目录下创建该文件,则Cypress将使用默认的配置选项运行测试。

三、生成配置对象

1、defineConfig函数介绍

defineConfig<ComponentDevServerOpts = any>(config: Cypress.ConfigOptions<ComponentDevServerOpts>): Cypress.ConfigOptions
  • defineConfig: 这是函数的名称。
  • <ComponentDevServerOpts = any>: 这是一个泛型参数,用于定义 defineConfig 函数的类型。ComponentDevServerOpts 是一个可选的泛型参数,它表示 Cypress 的组件开发服务器选项。= any 表示默认值为 any 类型,也就是如果没有提供类型参数,则默认为 any 类型。
  • (config: Cypress.ConfigOptions<ComponentDevServerOpts>): 这是函数的参数列表,它接受一个类型为 Cypress.ConfigOptions 的参数 configConfigOptions 是 Cypress 中的一个接口,定义了 Cypress 的配置选项。ComponentDevServerOpts 是上面提到的可选泛型参数,用于指定 Cypress 的组件开发服务器选项。
  • : Cypress.ConfigOptions: 这是函数的返回类型,它表示 defineConfig 函数返回一个类型为 Cypress.ConfigOptions 的值,即 Cypress 的配置选项对象。

        综上所述,defineConfig 函数接受一个 Cypress.ConfigOptions 类型的参数,返回一个 Cypress.ConfigOptions 类型的值,其中 ComponentDevServerOpts 是一个可选的泛型参数,表示 Cypress 的组件开发服务器选项,它的默认类型为 any

2、导入defineConfig函数

   defineConfig 是 Cypress 中的一个函数,它被用来定义 Cypress 的配置对象,可以用它来设置 Cypress 的默认配置或覆盖默认配置。

   defineConfig 函数是在 Cypress 6.0.0 版本中引入的,用于替代之前版本中的 configuration 函数。它可以被导出为一个单独的模块,在 Cypress 的配置文件中使用。

   defineConfig 函数接受一个对象作为参数,对象的属性就是 Cypress 的配置选项,例如 baseUrlviewportWidth 等等。可以在 Cypress 的文档中找到所有可用的配置选项和其说明。

需要注意的是,defineConfig 函数只能在 Cypress 的配置文件中使用,不能在测试文件中使用。

四、导出配置对象

配置文件必须叫这个名字 cypress.config.ts

        在 Cypress 中,defineConfig 函数用于定义 Cypress 的配置对象,它返回一个包含配置选项的对象。为了让 Cypress 使用这个配置对象,我们需要将它导出为一个模块,并将该模块的路径指定为 Cypress 的配置文件。

        在默认情况下,Cypress 会自动加载 cypress.config.ts 文件作为配置文件。但是,我们也可以使用 cypress/plugins/index.js 文件作为配置文件,并在其中使用 defineConfig 函数来定义配置对象。

        在 cypress/plugins/index.js 文件中,我们将 defineConfig 函数返回的配置对象通过 module.exports 导出为一个模块。这样,Cypress 在启动时会加载这个模块,并将其中定义的配置对象用于 Cypress 的运行。如果我们不导出配置对象,则 Cypress 会使用默认配置选项。

        因此,通过将 defineConfig 函数返回的配置对象导出为一个模块,我们可以自定义 Cypress 的配置选项,实现一些特定的功能或适应特定的场景。

第九章、Node.js 的事件监听器

        Node.js 的事件监听器是一种基于观察者模式的设计模式。它是 Node.js 中事件驱动编程的核心之一,它使得我们可以轻松地监听和响应事件。

        Node.js 中的事件监听器基于 EventEmitter 类。通过创建 EventEmitter 的实例,并使用 onaddListener 方法来注册事件监听器,我们可以监听任意事件,并在事件触发时执行相应的回调函数。

        Node.js 中的事件监听器可用于各种用途,例如处理 HTTP 请求、读取文件、处理数据库查询等等。它是 Node.js 中非常重要的编程范例之一,值得深入学习和掌握。

一、EventEmitter类介绍

EventEmitter 是 Node.js 中的一个核心模块,用于处理事件。它是一个类,继承自 EventEmitter 类的对象可以用于定义和触发事件。

EventEmitter 中,可以使用 on(eventName: string, listener: Function) 方法来定义事件监听器。其中,eventName 参数表示要监听的事件名称,listener 参数表示事件处理函数。当事件触发时,listener 函数会被调用,并传递相应的参数,例如错误信息、事件对象等等。

在使用 EventEmitter 时,可以通过 emit(eventName: string, ...args: any[]) 方法来触发事件。其中,eventName 参数表示要触发的事件名称,...args 参数表示传递给事件监听器的参数。

以下是一个使用 EventEmitter 的示例:

1、注册事件监听器

语法:

实例对象名.on(eventName:string ,  listener:function)

        其中,eventName 是事件名称,可以是任意字符串,通常用于描述事件的含义。listener 是事件监听器,可以是任何 JavaScript 函数,用于处理事件被触发时的逻辑。当 eventName 事件被触发时,所有注册的 listener 将按照注册的顺序被依次调用。

案例一:

例如,以下代码注册了一个 data 事件监听器:

const EventEmitter = require('events')
const myEmitter = new EventEmitter()

myEmitter.on('data', (data) => {
  console.log(`Received data: ${data}`)
})

        在这个例子中,我们使用 on 方法来注册了一个 data 事件监听器,该监听器接收一个数据参数,并将数据打印到控制台上。

案例二:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

// 定义事件监听器
myEmitter.on('event', (a, b) => {
  console.log(a, b, this);
  console.log(this == myEmitter);
});

2、触发事件

语法

实例对象名.emit(eventName: string, ...args: any[])

其中,eventName 是要触发的事件名称,...args 是传递给事件监听器的参数列表。当 eventName 事件被触发时,所有注册的监听器将按照注册的顺序被依次调用,并传递给它们相应的参数。

案例一:

例如,我们可以使用以下代码触发 data 事件:

myEmitter.emit('data', 'hello world')

        在这个例子中,我们使用 emit 方法触发了 data 事件,并传递了一个数据参数 'hello world'。当事件被触发时,注册的 listener 将被调用,并执行相应的操作。

案例二:

myEmitter.emit('event', 'hello', 'world');

在上述例子中,我们定义了一个 MyEmitter 类,该类继承自 EventEmitter,并使用 on 方法定义了一个 event 事件的监听器。然后,我们通过 emit 方法触发了 event 事件,并传递了两个参数。当事件触发时,事件监听器会被调用,并输出参数及 this 的值。

二、EventEmitter类的实例方法介绍

1、on()实例方法

on 方法是 EventEmitter 类的实例方法之一,用于添加事件监听器。

在 Node.js 中,EventEmitter 是一个核心模块,它提供了许多用于处理事件的方法。其中,on 方法用于向事件监听器数组中添加监听器。它的函数签名为:

emitter.on(eventName: string | symbol, listener: (...args: any[]) => void):

        其中,eventName 参数表示要监听的事件名称,可以是一个字符串或 Symbol 对象;listener 参数表示事件监听器函数,该函数会在事件被触发时执行。当事件被触发时,listener 函数会接收相应的参数,并执行相关的操作。 

在 Cypress 中,有两个对象提供了 on 方法,分别是 Cypresscy

  • Cypress.on(eventName: string, callback: Function):用于在 Cypress 运行时监听事件。可以监听的事件包括:before:run, run, after:run, test:before:run, test:after:run 等等。这些事件可以用于在 Cypress 运行过程中执行相关的操作,例如在测试运行前准备测试环境,或在测试运行后清理测试数据等等。

  • cy.on(eventName: string, callback: Function):用于在测试运行时监听事件。可以监听的事件包括:before:visit, visit, before:submit, submit, before:click, click 等等。这些事件可以用于在测试运行过程中模拟用户交互,例如点击按钮、提交表单等等。

        在 Cypress 的配置文件中,用户可以通过定义 setupNodeEvents 函数来监听 Node.js 运行时的事件,例如 uncaughtExceptionunhandledRejection 事件。在 setupNodeEvents 函数中,Cypress 会将一个 on 对象传递给用户,该对象是一个 EventEmitter,可以用于定义事件监听器。在此时,用户可以通过 on 对象调用 Cypress 提供的 on 方法来监听事件。

        因此,on 是 Cypress 提供的一个事件处理对象,可以通过 Cypress.oncy.on 方法来使用,在 Cypress 配置文件中的 setupNodeEvents 函数中,Cypress 会将一个 on 对象传递给用户,供用户定义事件监听器。

2、Cypress.on()方法和cy.on()方法详解

        在 Cypress 中,Cypress.on() 是 Cypress 提供的全局函数,用于注册 Cypress 内部的事件监听器,比如 before:runafter:run 等。因此,如果要监听 Cypress 内部的事件,必须使用 Cypress.on()

        而如果要监听自定义事件,可以使用任意名称作为事件名,并使用 cy.on() 函数进行注册。在这种情况下,cy.on()Cypress.on() 都可以使用,但建议使用 cy.on(),因为它更符合 Cypress 的测试范式,同时还可以在测试用例中使用 cy.trigger() 触发自定义事件。

因此,在上下文允许的情况下,建议根据事件类型使用 Cypress.on()cy.on() 函数。

cy.on()中 cy 不可省略。如果省略 cy,将会出现错误提示

在 Cypress 中,Cypress.on() 是 Cypress 对象提供的全局函数,用于注册 Cypress 内部的事件监听器。在使用 Cypress.on() 函数时,可以省略 Cypress 对象

、setupNodeEvents函数

1、运行原理

setupNodeEvents 是 Cypress 内部使用的一个函数,用于设置 Node.js 运行时的事件处理函数。它被定义在 lib/cypress.ts 文件中,并在 cypress.ts 文件中导出。在 Cypress 启动时,会调用 start 函数,该函数会调用 setupNodeEvents 函数来设置 Node.js 运行时的事件处理函数。具体的调用过程如下:

1、在 cypress.ts 文件中,首先导入 setupNodeEvents 函数:

import { setupNodeEvents } from './lib/cypress'

2、在 start 函数中,调用 setupNodeEvents 函数: 

const start = (options: Partial<CypressCommandLineOptions> = {}) => {
  // ...

  // 设置 Node.js 运行时的事件处理函数
  setupNodeEvents()

  // ...
}

        在调用 setupNodeEvents 函数之后,Cypress 会监听 Node.js 运行时的 uncaughtExceptionunhandledRejection 事件,并在这些事件发生时输出相关的错误信息。此外,Cypress 还会监听 Node.js 运行时的 SIGINTSIGTERM 事件,以便在用户终止 Cypress 运行时执行相关的清理操作。

2、参数分析

setupNodeEvents 函数并不是某个对象的方法,它是 Cypress 的一项配置选项,用于在 Cypress 运行时设置事件处理函数。

在 Cypress 中,setupNodeEvents 可以在 Cypress 配置文件中进行配置。当用户在配置文件中定义了 setupNodeEvents 函数时,Cypress 会在运行时调用该函数,

   setupNodeEvents 是 Cypress 的配置文件中的一个函数,用于在启动 Cypress 时注册 Node.js 的事件监听器。它会接收两个参数 onconfig

on 是Cypress对象的一个方法,此处省略了Cypress,用于注册事件监听器。Cypress 使用了许多事件来处理测试过程中的各个步骤,例如 before:run, after:run, before:spec, after:spec 等等。我们可以通过 on 对象来监听这些事件,并在事件发生时执行相应的操作。

由于 setupNodeEvents 函数是在 Node.js 环境下运行的,而不是在测试用例中运行,因此不能使用 cy.on() 函数。相反,应该使用 Cypress.on() 函数来注册 Cypress 内部的事件监听器,以便在 Node.js 环境下执行相关的操作。on 是Cypress对象的一个方法,此处省略了Cypress

config 则是 Cypress 的配置对象,包含了 Cypress 的所有配置选项,例如浏览器类型、测试超时时间、测试文件路径等等。我们可以通过 config 来修改 Cypress 的默认配置选项

在 Cypress 的配置文件中,可以直接使用 setupNodeEvents 函数,无需导入。

这是因为 Cypress 在运行配置文件之前,会将其加载到 Node.js 的上下文中执行。在加载配置文件时,Cypress 会将配置文件中的代码直接注入到 Node.js 的上下文中执行,这样就可以直接访问 Cypress 的全局变量和函数。因此,即使在配置文件中没有导入 setupNodeEvents 函数,也可以直接使用它。

四、运行级别的监听事件

Cypress中常见的事件如下:

  1. before:run: 在运行测试之前触发的事件,可用于在运行测试前设置一些全局的配置或进行一些初始化操作。

  2. after:run: 在测试运行结束后触发的事件,可用于在测试结束后进行一些资源清理或记录测试结果。

  3. before:each: 在每个测试用例运行之前触发的事件,可用于在每个测试用例执行前设置一些前置条件或进行一些初始化操作。

  4. after:each: 在每个测试用例运行结束后触发的事件,可用于在每个测试用例执行后进行一些资源清理或记录测试结果。

  5. test:before:run: 在每个测试用例运行前触发的事件,可用于在每个测试用例执行前设置一些前置条件或进行一些初始化操作。

  6. test:after:run: 在每个测试用例运行结束后触发的事件,可用于在每个测试用例执行后进行一些资源清理或记录测试结果。

  7. viewport:changed: 当 Cypress 视口的大小发生变化时触发的事件。

  8. window:before:load: 在 Cypress 加载页面前触发的事件,可用于在页面加载前设置一些前置条件或进行一些初始化操作。

  9. window:load: 在 Cypress 加载页面后触发的事件,可用于在页面加载后进行一些资源清理或记录测试结果。

  10. uncaught:exception: 当未捕获的异常发生时触发的事件,可用于捕获测试中的错误并进行处理。

以上是一些常见的事件,开发者还可以通过 Cypress.on() 方法自定义事件监听。

五、用例级别的监听事件

before:在所有测试用例运行之前运行的钩子事件。示例:

before(() => {
  // do something before all tests run
})

beforeEach:在每个测试用例运行之前运行的钩子事件。示例

beforeEach(() => {
  // do something before each test run
})

after:在所有测试用例运行之后运行的钩子事件。示例:

after(() => {
  // do something after all tests run
})

afterEach:在每个测试用例运行之后运行的钩子事件。示例:

afterEach(() => {
  // do something after each test run
})

cy.on():监听 Cypress 的事件。示例:

cy.on('fail', (error, runnable) => {
  // do something when a test fails
})

cy.once():只监听一次 Cypress 的事件。示例:

cy.once('window:load', () => {
  // do something when the page is loaded for the first time
})

cy.off():取消监听 Cypress 的事件。示例:

cy.off('fail', (error, runnable) => {
  // do something when a test fails
})

        总之,在 Cypress 中,事件监听是非常重要的功能,它可以帮助我们更好地监控测试的执行过程,从而更好地诊断和排查测试问题。在编写测试脚本时,应充分利用事件监听功能,编写出具有高可读性和可维护性的测试脚本。

六、监听事件触发分析

        事件是根据需求设计的。有些事件是在特定的情况下需要自动触发,比如 before:eachafter:each 事件,在每个测试用例执行前后自动触发,方便开发者在测试用例执行前后进行一些必要的初始化和清理工作。

        而有些事件是需要手动触发的,比如自定义事件监听,开发者可以在测试用例中手动触发自定义的事件,然后在事件监听函数中执行相应的逻辑。这种设计可以使开发者更灵活地控制事件的触发时机,符合测试用例的具体需求。

        举一个自定义事件监听的例子:假设我们有一个按钮,当点击该按钮时需要触发一个事件,并在事件监听函数中执行一些逻辑。我们可以在测试用例中手动触发该事件,然后在事件监听函数中执行相应的逻辑。

代码示例:

// 在测试用例中手动触发事件
cy.get('button').click().trigger('custom:event')

// 监听自定义事件
cy.on('custom:event', () => {
  // 在事件监听函数中执行逻辑
  console.log('custom event triggered')
})

        在上面的代码中,我们使用 trigger 方法手动触发了一个名为 custom:event 的自定义事件,并在事件监听函数中执行了一些逻辑。由于这是一个自定义事件,Cypress 并不会自动触发该事件,需要我们手动触发。

第十章、Cypress配置文件

        启动 Cypress 时,系统会引导用户完成一个向导,用于创建 Cypress 的配置文件。这个配置文件默认使用的是 JavaScript 的 CommonJS 模块语法,可以命名为 cypress.config.js。同时,也支持使用 TypeScript 编写的配置文件,命名为 cypress.config.ts

        TypeScript 是 JavaScript 的超集,这意味着 TypeScript 包含了 JavaScript 的所有特性和语法,同时还提供了一些额外的语言特性和工具,如类型系统和静态分析等。TypeScript 可以看作是对 JavaScript 的补充和扩展,它提供了一些额外的功能和工具,可以帮助开发人员编写更可靠、可维护、可扩展的代码。

        除了使用默认的 .js.ts 文件格式外,Cypress 还支持使用 .mjs.cjs 格式的配置文件。.mjs 格式的配置文件支持使用 ESM(ECMAScript Modules)模块语法,无需进行转译,.cjs 格式的配置文件则使用 CommonJS 模块语法,这是 JavaScript 文件的默认语法。

  .mjs.cjs 都是 JavaScript 文件的扩展名。

  .mjs 文件使用 ECMAScript 模块语法(ESM,即 ECMAScript Modules),并且可以在支持 ESM 的环境中直接运行,无需任何转换或编译。因此,.mjs 文件通常用于在 Node.js 的 ESM 模式下编写 JavaScript 模块,以及在浏览器端使用 JavaScript 模块化技术。

  .cjs 文件使用 CommonJS 模块语法,并且是 Node.js 中的默认模块格式。通常用于在 Node.js 中编写和加载 JavaScript 模块。和 .mjs 不同,.cjs 文件不能直接在浏览器端运行,需要使用打包工具进行转换和打包。

Cypress 支持使用这两种格式的配置文件,用户可以根据自己的需求选择适合自己的格式。

        如果您将测试结果记录到 Cypress Cloud 平台,那么配置文件中也将包含 projectId 字段,用于指定测试结果的存储位置。

本配置文件采用cypress.config.ts格式

一、导入Cypress默认配置函数

        defineConfig( ) 函数是 Cypress 测试框架中用于配置的函数,需要传入一个配置对象参数。通过导入 defineConfig 函数,你可以定义和配置 Cypress 的测试运行环境和行为。

        在 Cypress 中,你可以使用 defineConfig 函数来定义一个配置对象,该对象包含 Cypress 的各种配置选项。例如,你可以定义 baseUrl 选项来设置测试运行的基础 URL,或者定义 viewportWidthviewportHeight 来设置测试运行的浏览器视图大小等等。

        通过导入 defineConfig 函数并使用它来定义 Cypress 的配置,可以使你的测试更加可靠和灵活。同时,这也可以让你的测试代码更加模块化,使其更易于维护和重用。

1、导入常见问题

CommonJS语法,require报错

        需要为 Node.js 安装类型定义文件。它建议你运行 npm i --save-dev @types/node 命令来安装类型定义文件,然后在你的 tsconfig.json 文件中的 types 字段中添加 'node'。这将允许 TypeScript 编译器了解 Node.js 的类型和 API,从而使你的代码更容易进行类型检查和编辑。最后重启vscode

es6语法cypress模块不识别

然后在你的 tsconfig.json 文件中的 types 字段中添加 'cypress',定义cypress类型。

2、将配置文件修改后打不开cypress问题

  1. 您是否已经正确地安装了TypeScript和相关依赖项,例如 @types/cypress

  2. 确保您的tsconfig.json文件已正确配置,并且您的TypeScript版本与Cypress兼容。

  3. 如果您的Cypress版本低于6.0,那么您需要在cypress.json文件中指定 "baseUrl",否则它会报错。示例:"baseUrl": "http://localhost:3000"。

二、安装TS相关依赖项

要判断是否正确地安装了TypeScript和相关依赖项,可以执行以下步骤:

  1. 打开终端或命令提示符,并切换到项目根目录。

  2. 运行以下命令来检查是否已经安装了TypeScript:

tsc -v

如果 TypeScript 已经正确安装,则会显示安装的版本号。

     3. 运行以下命令来检查是否已经安装了 @types/cypress

npm list @types/cypress

如果安装了 @types/cypress,则会显示其版本号和依赖关系。

      4. 如果您使用了其他 TypeScript 相关依赖项,例如 @types/mocha@types/chai,可以使用类似的命令检查它们的安装情况。

如果您发现尚未安装某些依赖项,请使用以下命令安装它们:

npm install --save-dev <package-name>

npm install --save-dev typescript @types/cypress

三、env配置

        在 Cypress 中,env(也可以写作 Cypress.env())是一个全局函数,用于设置和获取环境变量。配置文件中的 env 属性是一个 JavaScript 对象,用于设置一些全局的环境变量。在测试用例中,可以通过 Cypress.env() 函数来获取这些全局的环境变量。需要注意的是,配置文件中设置的 env 属性的值可以在测试用例中被覆盖。

以下是一个使用 env 属性的示例:

cypress.config.ts 配置文件中,设置 env 属性如下:

{
  "env": {
    "username": "user",
    "password": "123456",
    "baseUrl": "https://example.com"
  }
}

在测试用例中,可以通过 Cypress.env() 函数来获取这些全局的环境变量,如下所示:

describe('Login test', () => {
  it('should log in successfully', () => {
    const username = Cypress.env('username');
    const password = Cypress.env('password');
    const baseUrl = Cypress.env('baseUrl');

    cy.visit(baseUrl);
    cy.get('#username').type(username);
    cy.get('#password').type(password);
    cy.get('#login-btn').click();

    // perform some assertions to ensure successful login
  });
});

        在上面的测试用例中,我们从全局的 env 属性中获取了用户名、密码和基本 URL,并使用这些值来进行登录操作。

第十一章、cypress项目结构

Cypress是一个用于Web应用程序测试的JavaScript端到端测试框架,它的目录结构如下:

cypress/
├── e2e/
├── fixtures/
├── plugins/
└── support/

一、e2e/

e2e/目录是Cypress框架中存放端到端测试用例的地方。在这个目录中,可以自由组织目录结构,创建多个测试文件,并在这些文件中编写测试用例。下面是关于e2e/目录的一些详细说明:

  • e2e/是端到端测试用例的默认目录,如果不指定cypress.config.ts文件中的testFiles属性,则Cypress会默认在这个目录下搜索测试文件。

  • e2e/目录中的测试文件通常以.cy.js结尾,例如login.cy.js

  • e2e/目录下可以创建子目录,例如dashboard/settings/,用于组织测试文件,使得测试用例更加清晰和易于管理。

  • Cypress允许在测试用例中编写JavaScript代码,并提供了一组API,可以用于模拟用户行为,例如cy.visit()用于访问一个网页,cy.get()用于获取元素,cy.click()用于模拟点击等。

  • 在测试用例中,开发人员可以编写多个测试场景,例如测试登录成功和失败两种情况。每个测试场景通常使用一个或多个cy.*()命令,用于模拟用户行为和断言测试结果。

  • Cypress支持自动重试测试用例,如果一个测试场景失败了,Cypress会自动尝试重新运行该场景,以确保测试结果的准确性。

  • Cypress还提供了丰富的调试功能,例如可以使用cy.pause()命令在测试过程中暂停执行,以便于开发人员进行调试。

        总之,e2e/目录是Cypress框架中最重要的目录之一,它存放了端到端测试用例的代码,开发人员可以在这里编写测试用例,对Web应用程序进行全面的测试。

二、fixtures/

fixtures/目录是Cypress框架中存放静态数据文件的地方。在测试用例中,通常需要使用一些静态数据文件,例如JSON、CSV、XML等格式的文件,这些文件可以包含测试数据、测试配置、模拟API响应等。下面是关于fixtures/目录的一些详细说明:

  • fixtures/目录是静态数据文件的默认目录,如果不指定cypress.json文件中的fixturesFolder属性,则Cypress会默认在这个目录下搜索静态数据文件。

  • fixtures/目录中,可以创建多个静态数据文件,这些文件通常以.json.csv.xml等格式结尾。

  • 在测试用例中,可以使用cy.fixture()命令加载静态数据文件,例如cy.fixture('users.json').then(users => { ... })可以加载名为users.json的JSON文件,并在回调函数中处理文件内容。

  • Cypress还支持使用别名的方式加载静态数据文件,例如可以在cypress.json文件中定义别名fixtures,然后在测试用例中使用cy.fixture('users.json').as('users')将文件内容保存到users别名中,以便后续使用。

  • 如果静态数据文件需要在测试用例中修改或使用动态值,可以在测试用例中使用cy.readFile()命令读取文件内容,然后使用JavaScript代码修改文件内容或提取动态值。

  • Cypress还支持使用beforeEach()before()钩子函数,在每个测试用例或测试套件运行前加载静态数据文件,并将文件内容保存到全局变量中,以便在后续的测试中使用。

        总之,fixtures/目录是Cypress框架中一个重要的目录,它存放了静态数据文件,可以在测试用例中使用cy.fixture()命令加载文件内容,并进行测试数据的准备工作,从而保证测试用例的正确性

三、plugins/

plugins/目录是Cypress框架中存放插件的地方。在Cypress框架中,插件是一些JavaScript代码,可以扩展Cypress的功能,例如添加自定义命令、修改Cypress配置、使用第三方库等。下面是关于plugins/目录的一些详细说明:

  • plugins/目录是插件的默认目录,如果不指定cypress.config.ts文件中的pluginsFile属性,则Cypress会默认在这个目录下搜索插件文件index.js

  • plugins/目录中,可以创建多个插件文件,例如index.jshelpers.js,用于组织插件代码。

  • Cypress插件可以使用module.exports关键字将插件代码暴露给Cypress,例如可以使用module.exports = (on, config) => { ... }将插件代码暴露给Cypress,并使用onconfig参数分别处理Cypress的事件和配置

  • 在插件中,可以使用on参数处理Cypress的事件,例如可以使用on('task', { myTask: myTaskHandler })注册一个自定义命令myTask,并在myTaskHandler函数中处理命令逻辑。

  • 在插件中,可以使用config参数读取和修改Cypress的配置,例如可以使用config.baseUrl获取当前测试的Base URL,或者使用config.env设置测试环境的变量。

  • Cypress插件还可以使用第三方库,例如可以在插件中使用require()import关键字导入第三方库,然后在插件中使用库的功能扩展Cypress。

总之,plugins/目录是Cypress框架中一个重要的目录,它存放了插件代码,可以扩展Cypress的功能,使得测试用例更加灵活和易于管理。

四、support/

support/目录是Cypress框架中存放支持文件的地方。在Cypress框架中,支持文件是指一些JavaScript代码,可以为测试用例提供支持,例如添加全局命令、设置测试配置、处理测试错误等。下面是关于support/目录的一些详细说明:

  • support/目录是支持文件的默认目录,如果不指定cypress.json文件中的supportFile属性,则Cypress会默认在这个目录下搜索支持文件index.js

  • support/目录中,可以创建多个支持文件,例如index.jscommands.js,用于组织支持代码。

  • Cypress支持文件可以使用module.exports关键字将支持代码暴露给Cypress,例如可以使用module.exports = { ... }将支持代码暴露给Cypress,并添加全局命令、设置测试配置等。

  • 在支持文件中,可以使用Cypress.Commands.add()命令添加全局命令,例如可以使用Cypress.Commands.add('login', (username, password) => { ... })添加名为login的全局命令,并在回调函数中处理登录逻辑。

  • 在支持文件中,可以使用Cypress.config()命令读取和修改Cypress的配置,例如可以使用Cypress.config('baseUrl', 'https://example.com')设置当前测试的Base URL。

  • Cypress支持文件还可以处理测试错误和异常,例如可以使用Cypress.on('uncaught:exception', (err, runnable) => { ... })注册一个事件监听器,捕获测试用例中的异常,并处理异常信息。

总之,support/目录是Cypress框架中一个重要的目录,它存放了支持文件,可以为测试用例提供支持和增强功能,从而使得测试用例更加稳定和可靠。

Cypress项目结构预览

cypress/
├── e2e/
│   ├── login.spec.js
│   ├── dashboard/
│   │   ├── overview.spec.js
│   │   └── transactions.spec.js
│   └── settings/
│       └── profile.spec.js
├── fixtures/
│   ├── users.json
│   └── products.csv
├── plugins/
│   ├── index.js
│   └── screenshots.js
└── support/
    ├── commands.js
    └── utils.js

五、低版本目录结构介绍

1、fixtures

  • 存放json格式的数据。一般可用作接口数据的参数准备,或者分模块的用例输入,方便后期维护。读取时使用cy.fixture('xxx/xxx/xxx.json'),返回整个json文件的对象。

2、intergration

  • 存放测试脚本,命名方式xxx.spec.js(后缀一定需要为.spec.js)。打开cypress的交互界面时,会自动读取该文件夹下的所有测试脚本。
  • 示例项目中,该文件夹下面举例了各类API的使用方法。需要用到时,可以参考学习。

3、plugins

  •  存放第三方插件或者自己编写的插件。

4、support

存放自定义方法:

1、index.js用于引入公共方法或者公共参数的js文件(index.js文件名不可变更)。

 2、commands.js用于封装公共方法,可自定义其他js文件,继续在index.js文件中引用即可。若未引用,则在其他地方不能直接调用

5、项目结构预览

三、初试文件

第十二章、自定义任务

一、介绍

        Cypress 允许用户在测试运行过程中定义自己的任务(task),以便在测试代码中调用这些任务。自定义任务可以用于执行一些特定的操作,例如上传文件、向远程服务器发送请求、从数据库中查询数据等。

自定义任务都是异步的

二、运行原理

        Cypress 中的自定义任务是基于 Node.js 的异步编程模型来实现的。当我们使用 cy.task 来调用自定义任务时,Cypress 会将任务名称和参数传递给插件系统,在插件系统中查找并执行相应的自定义任务。执行自定义任务时,Cypress 会创建一个新的 Node.js 进程,然后在该进程中执行自定义任务的代码。由于自定义任务通常是异步执行的,因此需要返回一个 Promise 对象,以便 Cypress 可以等待任务的完成。

        在自定义任务的代码中,我们可以使用 Node.js 提供的 API 来执行一些操作,例如访问文件系统、发送 HTTP 请求、操作数据库等。当任务完成时,我们可以使用 Promise 的 resolve 方法将结果返回给 Cypress,或者使用 reject 方法将错误信息返回给 Cypress。如果任务成功完成,Cypress 将在测试代码中继续执行后续的命令,如果任务失败,则会将错误信息传递给测试结果,以便我们进行调试和修复。

        总的来说,自定义任务的运行原理是基于 Node.js 的异步编程模型和 Promise 对象来实现的。通过自定义任务,我们可以扩展 Cypress 的功能,使其可以执行一些不支持的操作,例如访问文件系统、发送 HTTP 请求、操作数据库等。同时,自定义任务还可以在测试代码中实现复杂的异步逻辑,从而提高测试的灵活性和可维护性。

三、语法

        自定义任务应该在 Cypress 的支持文件中进行定义,通常是在 cypress/plugins/index.js 文件中定义。这个文件会在 Cypress 启动时自动加载,并允许我们对 Cypress 进行全局配置和扩展。

        我们可以使用 on 方法来定义自定义任务。该方法需要传入两个参数,第一个参数是任务名称,必须以方括号包含,并且只能包含字母、数字和下划线。第二个参数是一个函数,用于定义任务的实现逻辑。

module.exports = (on, config) => {
  on('task', {
    customTask: (arg1, arg2) => {
      // 执行任务逻辑
      return result
    }
  })
}

四、自定义任务的写法

        自定义任务通常需要在 Cypress 的插件文件中进行编写,插件文件的默认名称为 index.js。以下是一个示例:

const fs = require('fs')

module.exports = (on, config) => {
  on('task', {
    readFile(filePath) {
      return new Promise((resolve, reject) => {
        fs.readFile(filePath, 'utf8', (err, data) => {
          if (err) {
            reject(err)
          } else {
            resolve(data)
          }
        })
      })
    }
  })
}

        在这个示例中,我们定义了一个名为 readFile 的自定义任务,该任务用于读取指定路径下的文件,并返回文件内容。任务的实现过程中,我们使用了 Node.js 内置的 fs 模块来读取文件,并将读取到的内容通过 Promise 对象返回。

五、自定义任务的调用方法

        在测试代码中调用自定义任务时,可以使用 cy.task() 方法来执行任务。cy.task(taskName, ...args) 方法接受两个参数,第一个参数是自定义任务的名称,第二个参数(可选)是传递给任务函数的参数。

以下是一个示例:

describe('readFile task', () => {
  it('should read file content', () => {
    cy.task('readFile', 'path/to/file.txt').then(content => {
      expect(content).to.equal('Hello, world!')
    })
  })
})

        在这个示例中,我们调用了名为 readFile 的自定义任务,并传入文件路径作为参数。在任务执行完毕后,我们使用 then() 方法来处理任务的返回值,并使用 expect() 断言来验证返回值是否符合预期。

七、自定义任务的回调函数分析

1、回调函数返回值分析

        在 Cypress 中,任务回调函数返回值是有一定要求的。

        根据官方文档,任务回调函数必须     返回一个值、null  或者  一个 Promise 对象,  表示任务的处理结果。如果任务回调函数返回 undefined,将会触发错误:The task 'taskName' returned undefined. You must return a value, null, or a promise that resolves to a value or null to indicate that the task was handled.

        在返回 null 的情况下,任务将被视为成功处理,但是不会有任何返回值。

        在返回 Promise 对象的情况下,Cypress 会等待 Promise 对象 resolve 或 reject 后,根据 resolve 或 reject 的值来判断任务处理成功或失败。如果 resolve 的值是 null,则任务成功处理,否则任务处理失败。

        因此,对于一个任务回调函数,必须确保返回一个值、null 或者一个 Promise 对象,以避免出现上述错误。

2、console.log语句分析

        Cypress中的插件文件index.js运行在Node.js环境中,而非浏览器环境。这是因为Cypress的架构采用了双进程模型,其中主进程负责控制浏览器的行为,测试代码则运行在Node.js进程中。插件文件作为测试代码的补充,自然也是运行在Node.js进程中的。因此,在插件文件中可以使用Node.js的API和语法,但无法直接访问浏览器的DOM。

  console.log()语句在Cypress中的插件文件index.js中是运行在Node.js环境中的,而非浏览器环境中。插件文件运行在Node.js进程中,可以使用Node.js的API和语法,但无法直接访问浏览器的DOM。因此,插件文件中的console.log()语句将在Node.js的控制台中输出,而不是在浏览器的控制台中输出。

cypress/plugins/index.js
on('task', {
        aaa(message) {
            console.log(message);
            return null;
        }
    });

Node.js控制台输出

cypress/e2e/demo/demo1.cy.ts
describe('My Test Suite', () => {

  it('My Test Case', () => {
    cy.task('aaa','贾卓群');
  })
})

执行命令

npx cypress run --spec "cypress\e2e\demo\demo1.cy.ts" --headless --browser chrome

浏览器控制台输出

describe('My Test Suite', () => {

  it('My Test Case', () => {
    cy.task('aaa', '贾卓群').then(function (result) {
      console.log("doneCallback: " + result);
    })
  })

})

六、自定义任务的注意事项

在编写和使用自定义任务时,需要注意以下几点:

  • 自定义任务必须在 Cypress 插件文件中定义,并通过 on('task', {taskName: taskFunction}) 的方式注册到 Cypress 中。
  • 自定义任务可以接受任意类型和数量的参数,并返回Promise 对象。
  • 在测试代码中调用自定义任务时,可以使用 cy.task() 方法,并通过 Promise 的方式处理任务的返回值。
  • on('task', {...})的对象中,可以定义多个自定义任务,每个任务需要指定一个唯一的任务名称,并提供任务函数。任务函数接受参数并返回一个Promise对象,以便在任务完成时能够返回结果。
  • 自定义任务中的异步操作必须通过 Promise 的方式来处理
  • 自定义任务名称必须是唯一的,不能与其他任务名称重复。
  • 自定义任务函数必须返回一个Promise对象。
  • 在测试用例中调用自定义任务需要使用cy.task()函数,并且需要使用.then()方法来获取返回的结果。
  • 自定义任务中使用的所有外部库都需要在插件文件中引入。

第十三章、plugins

cypress/plugins/index.js,运行在Node.js环境中

一、插件文件介绍

   cypress/plugins/index.js 是 Cypress 的插件文件,它用于在 Cypress 测试运行期间自定义行为和功能。Cypress 插件提供了一种方便的方式来扩展 Cypress 的功能,例如添加自定义命令、处理访问网络请求的拦截器、在测试运行期间注入代码等等。

        在 cypress/plugins/index.js 中,你可以定义各种插件函数,这些函数可以在测试运行期间被 Cypress 调用。例如,你可以使用 on 函数来监听 Cypress 的各种事件(例如 before:browser:launchbefore:run),也可以使用 task 函数来定义自定义命令。

        插件文件的位置默认情况下是在项目根目录的 cypress/plugins/index.js,但也可以在 cypress.config.ts 文件中配置其他位置。

        总之,cypress/plugins/index.js 是 Cypress 的重要文件之一,可以用于扩展 Cypress 的能力,实现各种定制化的需求。

        虽然默认情况下 Cypress 会在项目根目录下寻找名为 cypress/plugins/index.js 的插件文件,但实际上你可以在 cypress.config.ts 文件中配置插件文件的位置和名称,只要指定正确的路径即可。

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

        在 TypeScript 中,/// <reference types="module" /> 或者 /// <reference types="package" /> 语法用于声明依赖类型定义文件,它告诉编译器当前文件需要使用某个模块或包的类型定义文件,以便在编译期间获取该模块或包的类型信息。这个语法通常用于声明全局变量或函数的类型。

        对于 Cypress 来说,我们可以使用 /// <reference types="cypress" /> 语法来引入 Cypress 的类型定义文件。这个文件中定义了 Cypress 的所有类型,包括 cy 对象、Cypress 对象以及其他相关的类型。引入这个类型定义文件可以使编译器更好地理解我们的代码,并提供相应的类型检查和自动补全功能。

        通常情况下,我们在 TypeScript 代码的顶部添加 /// <reference types="cypress" /> 语句,以便获取 Cypress 的类型定义文件。例如:

// <reference types="cypress" />

describe('My Test Suite', () => {
  it('My Test Case', () => {
    cy.visit('/')
    cy.get('button').click()
    cy.get('#message').should('have.text', 'Hello, World!')
  })
})

        在上面的代码中,我们添加了 /// <reference types="cypress" /> 语句,并在测试用例中使用了 Cypress 的 API。这样,在编译期间,TypeScript 编译器就能获取 Cypress 的类型定义文件,并为我们提供相应的类型检查和自动补全功能。

        需要注意的是,/// <reference types="cypress" /> 语句只能用于 TypeScript 文件中,它不会影响 JavaScript 文件的编译。如果你使用 JavaScript 编写 Cypress 测试用例,可以通过 importrequire 语句来引入 Cypress 对象,并进行类型注释来实现类型检查和自动补全功能。

/// <reference types="cypress" /> 是用于在 TypeScript 中声明 Cypress 类型的语法,可以确保在代码中正确地使用 Cypress API。

        虽然这个语法通常出现在 TypeScript 文件中,但是它也可以在 JavaScript 文件中使用。如果你正在使用 JavaScript 编写 Cypress 测试,并且想要在测试文件中使用 /// <reference types="cypress" />,你需要先确保安装了 @types/cypress 模块,这个模块包含了 Cypress 的类型声明文件。然后,你可以在 JavaScript 文件的顶部添加 /// <reference types="cypress" />,以获取 Cypress 的类型提示和补全。

        需要注意的是,在 JavaScript 文件中使用 /// <reference types="cypress" /> 只是为了获得类型声明文件的支持,它并不会将 JavaScript 文件转换为 TypeScript 文件。如果你想要使用 TypeScript 的其他特性,如类型检查、类型推断等,你需要将文件的扩展名改为 .ts,并且使用 TypeScript 语法编写代码。

三、代码解释

1、on(after:spec,callback function)

on('after:spec', (spec, results) => {
        if (results.stats.failures === 0 && results.video) {
            return del(results.video)
        }
    })

        用于在测试运行结束后,检查测试结果是否成功并删除测试过程中可能生成的视频文件。

        Cypress 框架会在测试用例运行期间,将所有测试运行的结果记录在一个对象中,该对象包含了测试用例的状态、统计信息、错误信息等。其中,results.stats.failures 属性记录了测试用例运行期间失败的次数,如果该属性值为0,说明所有测试用例都运行成功了。此外,如果测试用例中使用了 cy.screenshot()cy.record() 命令录制视频,则 results.video 属性会记录录制视频的文件路径。

        因此,这段代码的逻辑是:当所有测试用例都运行成功,并且存在录制的视频文件时,就使用 del 模块删除该视频文件。由于 del 模块返回一个 Promise 对象,因此代码中使用了 return 关键字将该 Promise 对象返回,以便等待 Promise 对象完成后继续执行其他代码。

2、after:run监听解释

on('after:run', () => {
        if (config.env.racetrackLog){
          return new Promise((resolve, reject) => {
            testSetId = config.env.racetrackTestSetId
    
            console.log("testSetID in testSetEnd: " + testSetId)
            if (testSetId === null) {
              console.log("There is no valid testSetID in testSetEnd: " + testSetId)
              return reject()
            } else {
              const params = {
                "ID": testSetId
              }
    
              return request.post({
                url: raceTrackServer + 'TestSetEnd.php',
                formData: params
              },(err, httpResponse, body) => {
                if (err) {
                  return reject("testSetEnd error:" + err)
                }
                return resolve(body)
              })
            }
          })
        }
      })

        这段代码是 Cypress 插件中的一个监听器,它会在所有测试用例运行完成之后执行。具体来说,这个监听器会在 after:run 事件上注册一个回调函数,当所有测试用例运行完成后,该回调函数就会被执行。

        在这个回调函数中,首先判断了一个环境变量 racetrackLog 是否为真,如果为真,就继续执行下面的代码,否则直接结束。接下来,获取了一个环境变量 racetrackTestSetId,该变量用于记录当前测试用例所属的测试集的 ID。如果这个变量为 null,就会打印一条错误信息,并使用 Promise 的 reject 方法来标记任务失败。否则,使用 request.post 方法向指定的 URL 发送一个 HTTP POST 请求,并将测试集的 ID 作为参数传递给服务器。如果请求成功,就使用 Promise 的 resolve 方法将服务器返回的结果传递给 Cypress。如果请求失败,就使用 reject 方法将错误信息传递给 Cypress。

        总的来说,这段代码的作用是在所有测试用例运行完成之后,向指定的服务器发送一个测试集结束的请求,并将测试集的 ID 作为参数传递给服务器。这个操作通常用于将测试结果上传到测试管理系统中,以便我们可以对测试结果进行管理和分析。同时,这段代码还展示了如何在 Cypress 插件中使用 Promise 和异步编程模型来实现复杂的操作。

3、racetrackUploadScreenshot 自定义任务解释

//自定义监听任务
    on('task', {
        //任务回调函数,传入一个简写形式的对象作为形参
        racetrackUploadScreenshot: ({ description, testCaseId, screenshotPath, screenshotName, raceTrackServer }) => {
            //返回一个promise对象
            return new Promise((resolve, reject) => {
                //输出截屏路径和截屏文件名
                console.log(screenshotPath + screenshotName)
                //判断指定路径下的文件存在,则执行
                if (fs.existsSync(screenshotPath + screenshotName)) {
                    //创建请求体
                    const formData = {
                        //描述
                        "Description": description,
                        //结果ID
                        "ResultID": testCaseId,
                        //截屏,创建可读文件流
                        "Screenshot": fs.createReadStream(screenshotPath + screenshotName),
                        //文件名
                        "filename": screenshotName
                    }
                    //给服务器发送post请求
                    return request.post({
                        //定义url地址
                        url: raceTrackServer + 'TestCaseScreenshot.php',
                        //请求体
                        formData: formData
                    }, (err, httpResponse, body) => {
                        //判断有错误的话执行
                        if (err) {
                            console.log("Upload screenshot error:" + err)
                            //将promise对象置为  失败  状态
                            return reject()
                        }
                        //将promise对象置为  成功  状态
                        return resolve(body)
                    })
                } else {
                    //判断指定路径下的文件不存在,则执行
                    console.log("Screenshot not exists")
                    //将promise对象置为  成功  状态
                    return resolve("Screenshot not exists")
                }
            })
        }
    })

        这段代码是在 Cypress 中定义一个名为 racetrackUploadScreenshot 的自定义任务。通过监听 task 事件并传入一个包含任务名和处理程序的对象,我们可以将自定义任务注册到 Cypress 中。在这种情况下,当我们在测试中调用 cy.task('racetrackUploadScreenshot', {...}) 时,该处理程序将被执行。

        这个自定义任务的处理程序接受一个包含任务调用参数的对象,并返回一个 Promise 对象。在处理程序中,我们首先检查要上传的截图文件是否存在。如果存在,我们将创建一个包含截图文件和其他数据的表单,并使用 request.post() 函数将其上传到指定的 raceTrackServer 地址。在上传完成后,我们将 resolve() 函数调用以将上传结果返回给调用方。

        如果截图文件不存在,我们将 resolve() 函数调用以返回一个带有相应消息的字符串。如果上传过程中发生错误,我们将 reject() 函数调用以返回一个错误对象。

        总之,这段代码展示了如何在 Cypress 中创建一个自定义任务,以处理测试中的特定功能或业务需求,以及如何使用外部库(fsrequest)与其他系统进行交互。

  { description, testCaseId, screenshotPath, screenshotName, raceTrackServer } 是一个对象的简写形式,也称为对象字面量。它可以用来快速创建一个包含多个属性的 JavaScript 对象,并将其作为参数传递给函数或方法。

        在这个例子中,这个对象包含了五个属性,分别是 descriptiontestCaseIdscreenshotPathscreenshotNameraceTrackServer。这些属性的名称对应了处理程序中接受的参数名称,并且它们的值可以根据具体需求在调用 cy.task() 方法时设置。

        使用对象字面量可以使代码更加简洁和易读,并且可以减少不必要的变量定义和赋值。它也是 JavaScript 中常用的一种对象创建方式,常见的用法包括定义常量、选项对象和命名参数等。

第十四章、自定义命令

一、介绍

        在 Cypress 中,自定义任务(Custom Task)和自定义命令(Custom Command)都可以用于在测试脚本中添加自定义逻辑和功能,但它们的使用场景和用途略有不同。

        自定义任务是一个函数,它可以接受一些参数,并返回一个 Promise,该 Promise 在任务完成时解析。任务可以用 cy.task() 命令调用。自定义任务通常用于在测试运行过程中执行一些异步操作,如启动一个服务器、读取一个文件、处理一些数据等等。自定义任务的作用域是整个测试运行过程,而不是单个测试用例。

        自定义命令是一个函数,它会通过 Cypress.Commands.add() 方法添加到 Cypress 命令集中,以便在测试脚本中使用。自定义命令通常用于封装常用的测试步骤或操作,以便在测试脚本中更加简洁和可读。自定义命令只能在测试脚本中使用,作用域仅限于当前测试用例。

        在某些情况下,自定义任务和自定义命令可以互相替代。例如,可以将一些常用的测试步骤封装为自定义任务,然后通过自定义命令来调用这些任务。这样可以使测试脚本更加简洁和可维护。

        总的来说,自定义任务和自定义命令都是 Cypress 提供的扩展机制,可以根据需要灵活地选择使用。自定义任务可以用于执行一些异步操作,自定义命令可以用于封装常用的测试步骤。两者都可以提高测试脚本的可读性和可维护性。

二、运行原理

        Cypress 中自定义命令的运行原理是基于 JavaScript 的原型继承机制。通过 Cypress.Commands.add() 方法定义的自定义命令将会被加入到 Cypress 的全局命令中。因此,自定义命令可以被整个测试项目中的所有测试用例共享和调用。

        在 Cypress 中,自定义命令也是一个函数,可以接收参数和返回值。在使用自定义命令时,Cypress 会将自定义命令的参数传递给函数,并执行函数内部的代码逻辑。执行结束后,可以通过函数的返回值来进行后续的链式调用。

举个例子,假设我们在 cypress/support/commands.js 中定义了如下的自定义命令:

Cypress.Commands.add('login', (username, password) => {
  cy.visit('/login')
  cy.get('#username').type(username)
  cy.get('#password').type(password)
  cy.get('#login-btn').click()

  return cy.get('.navbar')
})

        当我们在测试代码中调用该自定义命令时,Cypress 会将 usernamepassword 作为参数传递给自定义命令的函数,并执行函数内部的代码逻辑。执行结束后,可以通过 cy.get('.navbar') 的返回值进行后续的链式调用。

例如,我们可以这样使用自定义命令:

cy.login('myusername', 'mypassword')
  .contains('Profile')
  .click()

        这里的 contains()click() 都是 Cypress 的内置命令,而 login() 则是我们自定义的命令。Cypress 会按照链式调用的顺序依次执行这些命令,并返回最终的结果。

三、Cypress.Commands.add()

1、语法

该方法用于在 Cypress 中添加自定义命令,其语法如下:

Cypress.Commands.add(name, callbackFn)
Cypress.Commands.add(name, options, callbackFn)

2、用法

        举个例子,比如我们想自定义一个命令来获取并验证当前页面的标题,我们可以这样写:

        实现函数可以使用 Cypress 的命令,例如 cy.get()cy.contains()cy.request() 等来实现自定义的操作。实现函数可以接受参数,参数可以是常规值、函数或对象。

Cypress.Commands.add('checkTitle', () => {
  cy.title().should('not.be.empty')
})

        这样就定义了一个名为 checkTitle 的自定义命令。接下来,在我们的测试代码中,就可以使用这个自定义命令来检查页面的标题是否为空:

it('checks the title', () => {
  cy.visit('/')
  cy.checkTitle()
})

        这样就会在 Cypress 中执行 cy.title().should('not.be.empty') 操作,以检查页面标题是否为空。可以看到,通过自定义命令,我们可以将一系列常见的操作封装成一个自定义命令,从而提高测试代码的可读性和可维护性。

3、参数

3.1、name (String)

自定义命令的名称;

3.2、callbackFn (Function)

1、接受参数并执行自定义命令的函数;

2、回调函数的参数就是该命令所需的参数

  • 直接使用cy调用:则回调函数的参数就是调用该命令时所需参数
  • 通过回调连调用:除了subject参数外,其余参数就是调用该命令时所需参数

3.3、options (Object)

一个对象,可以定义自定义命令的行为。

options 中,可以定义 prevSubject 属性,它接受以下值:

  • false:忽略任何先前的 subject(父命令);
  • true:接收先前的 subject(子命令);
  • optional:可以开始一个新的链或使用现有的链(双重命令)。

除了控制命令的隐式行为外,还可以添加声明性主题验证,如:

  • element:需要先前的 subject 为 DOM 元素;(适用于回调链)
  • document:需要先前的 subject 为文档对象;
  • window:需要先前的 subject 为 window 对象。

四、Cypress.Commands.overwrite()

Cypress.Commands.overwrite() 方法可以用来覆盖已存在的自定义命令。它接受两个参数:命令的名称和命令的实现。在调用该方法时,Cypress 会检查当前是否已经有同名的命令存在。如果存在,则该方法将覆盖它,否则将创建一个新的命令。

以下是 Cypress.Commands.overwrite() 方法的参数详解:

  • commandName:要覆盖的自定义命令的名称。
  • callbackFn:一个回调函数,用于实现命令的具体功能。该函数将接收与自定义命令相同的参数。如果该函数返回一个值,则该值将成为该命令的结果。如果该函数抛出一个错误,则该错误将作为该命令的失败原因。
    • 原始命令
    • 原始命令中的参数

下面是一个简单的示例,演示如何使用 Cypress.Commands.overwrite() 方法覆盖默认的 cy.visit() 命令:

Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
  console.log(`Visiting ${url} with options`, options)
  return originalFn(url, options)
})

这三个参数指的是 Cypress.Commands.overwrite() 方法中的参数:

  • originalFn: 原始的 Cypress 命令函数,它是被 overwrite 的命令函数。可以使用这个函数来执行原始的命令,也可以选择不使用。
  • url: 用于重写的新 URL 地址。当 Cypress 命令中的 URL 和该参数匹配时,就会触发 overwrite 的函数。
  • options: 用于传递额外参数的对象。这个对象的内容取决于被 overwrite 的命令。一些命令需要额外的参数来正常工作,可以使用这个参数来传递这些参数。

      在上面的示例中,我们首先调用了 Cypress.Commands.overwrite() 方法,并将 'visit' 作为第一个参数传递。接下来,我们传递了一个回调函数,该函数接收 urloptions 参数,并在控制台上记录访问的 URL 和选项。最后,我们调用了原始的 cy.visit() 方法,并将 urloptions 作为参数传递。如果我们不调用原始的方法,那么就无法继续执行测试了。

        通过使用 Cypress.Commands.overwrite() 方法,我们可以轻松地修改 Cypress 中默认的命令,或者添加自己的自定义命令。

第十五章、support

        Cypress 框架中的 cypress/support/ 目录下的文件都是运行在 Node.js 环境中的。在这个目录下,可以创建 commands.js 文件来定义自定义命令,也可以创建 index.js 文件来导入其他自定义模块,这些文件都是运行在 Node.js 环境中的。此外,在这个目录下还可以创建 page_objects 目录来存放页面对象模型(Page Object Model)相关的文件,也是运行在 Node.js 环境中的。

一、global-utl.ts

1、自定义命令“selectNth”解释

Cypress.Commands.add('selectNth',
    { prevSubject: 'element' },
    (subject, pos, callback) => {
        cy.wrap(subject)
            .children('option')
            .eq(pos)
            .then(e => {
                cy.wrap(subject).select(e.text().trim(), { force: true })
                callback(e.text().trim())
            })
    })

        这部分代码使用 .then() 来处理前一个 Cypress 命令的结果,并在处理后执行一个回调函数。具体来说,这段代码是在执行 cy.wrap(subject).children('option').eq(pos) 命令后被执行的,而这个命令会返回找到的第 pos 个子元素,也就是一个 option 元素的 DOM 对象。

  .then() 接受一个回调函数,这个回调函数的参数 e 就是上一个命令返回的结果,也就是找到的 option 元素的 DOM 对象。这个回调函数首先对 e 调用 .text() 方法来获取这个 option 元素的文本内容,再调用 .trim() 方法来去掉首尾的空格,这样就得到了 option 元素的文本内容的字符串形式。

        在这个回调函数中,e 是通过 .eq() 选择器选中的某个 <option> 元素对象,这个元素对象是一个 jQuery 对象,因此可以调用 jQuery 对象的方法。其中,.text() 方法用于获取元素的文本内容,而 .trim() 方法用于去掉字符串的首尾空格,这些方法都是 jQuery 对象提供的。所以在这个回调函数中可以直接调用这些方法。

        接着,这个回调函数对 subject(即前一个命令中传入的 DOM 元素对象)调用 .select() 方法,将找到的 option 元素选中。.select() 方法中,我们将找到的 option 元素的文本内容作为第一个参数传入,使用 {force:true} 选项来强制选择这个 option 元素(即使它被禁用或不可见)。最后,回调函数执行一个函数 callback(e.text().trim()),将找到的 option 元素的文本内容作为参数传入。这个函数是可选的,根据使用场景来决定是否需要执行这个回调函数。

        总体来说,这个自定义命令的作用是从一个包含多个 option 元素的 select 元素中选择指定位置的 option 元素,并将其选中。选中后,这个自定义命令会返回选中的 option 元素的文本内容,可以用于之后的断言或其他操作。

调用该命令

cy.get(editVmSettingsDlg.memoryUnitDropdown).selectNth(1,(selectedOptionText)=>{
			this.racetrack.log(`-- Select memory unit: ${selectedOptionText}`)
		})

2、自定义命令“expandSubItems”解释

//自定义命令:expandSubItems
Cypress.Commands.add('expandSubItems', function (subItems, toggleSelector) {
    //获取可迭代元素
    cy.get(subItems)
        //遍历可迭代元素中的每一个子元素
        .each(($subItem) => {
            //拦截该 get 请求,并起名
            cy.intercept("GET", "/ui/tree/children?treeId=vsphere.core*").as("getTreeNodes")
            //将 DOM元素对象 转换为cy对象 ,并查找其后代元素,点击
            cy.wrap($subItem).find(toggleSelector).click()
            //等待 该资源 解析完成
            cy.wait("@getTreeNodes")
        })
})

这段代码定义了一个自定义命令 expandSubItems。让我们逐步解释它的功能:

  1. 首先,使用 Cypress.Commands.add() 方法定义了一个名为 expandSubItems 的自定义命令。该命令接受两个参数:subItemstoggleSelector

  2. 在命令的实现中,首先使用 cy.get(subItems) 获取与 subItems 选择器匹配的元素集合。这里假设 subItems 是一个用于定位子项的选择器。

  3. 使用 .each() 方法遍历每个匹配的子项元素 $subItem

  4. 在每个子项元素上执行一系列操作:

    • 使用 cy.intercept() 拦截网络请求,并使用 .as() 方法为请求命名为 "getTreeNodes"
    • 使用 cy.wrap($subItem).find(toggleSelector) 找到子项元素中的匹配 toggleSelector 的元素,并调用 .click() 方法触发点击事件。
    • 使用 cy.wait("@getTreeNodes") 等待命名为 "getTreeNodes" 的请求完成。

通过以上代码,自定义命令 expandSubItems 实现了一个功能:在每个子项元素上点击特定的 toggle 元素,并等待与之相关的网络请求完成。

subItemstoggleSelectorexpandSubItems 命令的两个参数,用于指定要操作的子项元素和用于展开子项的切换按钮的选择器。

  1. subItems:这是一个用于定位子项元素的选择器。它可以是任何有效的 CSS 选择器,用于匹配需要展开的子项元素。在命令的实现中,使用 cy.get(subItems) 获取与选择器匹配的子项元素集合。

    例如,如果子项元素具有特定的类名 .sub-item,则可以将 '.sub-item' 作为 subItems 的参数。

  2. toggleSelector:这是用于定位用于展开子项的切换按钮的选择器。它也是一个有效的 CSS 选择器,用于匹配在子项元素中触发展开操作的切换按钮。在命令的实现中,使用 cy.wrap($subItem).find(toggleSelector) 在每个子项元素内查找匹配的切换按钮。

    例如,如果切换按钮具有特定的类名 .toggle-button,则可以将 '.toggle-button' 作为 toggleSelector 的参数。

        通过将这两个参数传递给 expandSubItems 命令,可以灵活地指定要操作的子项元素和用于展开子项的切换按钮的方式。这使得命令可以适用于不同的场景和不同的页面结构,从而提高了代码的可重用性和灵活性

这个自定义命令可以在测试中使用,以简化代码和提高可重用性。例如:

cy.expandSubItems('.parent-element .sub-item', '.toggle-button')

        在上述示例中,我们使用自定义命令 expandSubItems.parent-element 中的每个 .sub-item 子项元素执行操作。其中,.toggle-button 是用于展开子项的按钮的选择器。通过使用自定义命令,我们可以更清晰地表达测试的意图,并避免在多个地方重复编写相同的操作逻辑。

 第十六章、Cypress命令对象(cy)

        Cypress命令对象就是Cypress的全局对象cy。cy对象包含了Cypress提供的所有命令,可以用于执行各种各样的操作,比如访问页面、查找元素、模拟用户行为、断言结果等等。使用Cypress命令对象的优点是,它们可以很好地与Cypress的异步执行模型配合使用,以确保测试执行的正确性和可靠性。

        在 Cypress 中,命令对象是 Cypress 的核心对象之一,它是用于在测试中进行交互的基本工具。通过命令对象,我们可以操作页面上的元素,模拟用户行为,获取元素的属性和状态,等等。在 Cypress 中,每个命令都会返回一个命令对象,这个对象包含了该命令的所有信息和状态。我们可以通过链式调用多个命令对象,实现一系列的操作和断言。

举例来说,我们可以使用以下命令对象进行页面元素的交互和断言:

cy.get('#search-input').type('cypress').should('have.value', 'cypress');
cy.get('#search-button').click();
cy.url().should('include', '/search?q=cypress');
cy.get('.search-results').should('have.length.gt', 0);

        上面的代码中,我们首先使用 cy.get() 命令对象获取了一个 ID 为 search-input 的输入框元素,并使用 .type() 方法对其进行输入操作,然后使用 .should() 方法进行断言。接下来,我们使用 cy.get() 命令对象获取了一个 ID 为 search-button 的按钮元素,并使用 .click() 方法对其进行点击操作。然后,我们使用 cy.url() 命令对象获取当前页面的 URL,并使用 .should() 方法进行断言。最后,我们使用 cy.get() 命令对象获取一个 class 为 search-results 的元素,并使用 .should() 方法进行断言。

        在这个例子中,每个命令都返回一个命令对象,我们可以使用链式调用连接这些命令对象,并进行一系列的操作和断言。通过命令对象,我们可以很方便地编写和维护我们的测试用例。

一、cypress命令对象全都是异步的

        Cypress中的所有命令都是异步的,并且会返回一个promise对象。这个promise对象代表了命令的执行状态,可以通过.then()方法来注册回调函数,处理命令执行成功的情况,也可以通过.catch()方法来注册回调函数,处理命令执行失败的情况。除此之外,还可以使用async/await语法来处理异步命令,使得代码更加简洁易读。

第十七章、jQuery 对象

        jQuery对象是指用jQuery语法选择DOM元素并对其进行操作的对象,jQuery 对象是由 jQuery 库封装的一组 DOM 元素,它们具有一些特殊的属性和方法,使得对这些元素进行操作更加方便和高效。在 jQuery 中,可以使用 $()jQuery() 函数来创建 jQuery 对象。

一、jQuery语法介绍

        jQuery是一个JavaScript库,提供了一组简洁的语法来选择、操作和遍历HTML文档中的元素,它简化了JavaScript代码的编写,并增强了交互式网页的动态性和易用性。

以下是jQuery常用的语法:

选择器:使用选择器来选择元素。例如,使用“#id”选择器选择id为“id”的元素:

$("#id")

      1、DOM遍历:使用jQuery对象的方法来遍历DOM树。例如,使用“next()”方法来选择下一个兄弟元素:

$("cssSelector").next()

      2、事件处理:使用“on()”方法来绑定事件处理程序。例如,使用“click”事件处理程序处理单击事件:

$("cssSelector").on("click", function() {
    // 处理代码
})

        3、属性和内容操作:使用jQuery对象的方法来获取或设置元素的属性和内容。例如,使用“text()”方法获取或设置元素的文本内容:

$("cssSelector").text("new text")

        4、CSS操作:使用jQuery对象的方法来获取或设置元素的CSS属性。例如,使用“css()”方法来获取或设置元素的背景颜色 

$("cssSelector").css("background-color", "red")

创建 jQuery 对象的最常见的方式是使用 CSS 选择器来选择一个或多个 DOM 元素,例如:

var $myDiv = $('#myDiv'); // 选择 ID 为 myDiv 的元素
var $myLinks = $('a'); // 选择所有链接元素

        在上面的例子中,$myDiv$myLinks 都是 jQuery 对象,它们分别包含了 ID 为 myDiv 的元素和所有链接元素。jQuery 对象实际上是一个类数组对象它包含了一个或多个原生 DOM 元素,并提供了一系列方法来对这些元素进行操作,例如修改属性、样式、内容、绑定事件等。

        需要注意的是,jQuery 对象并不是原生 DOM 元素,它只是对 DOM 元素的一层封装。如果需要访问原生 DOM 元素的属性或方法,可以使用 get() 方法来获取对应的 DOM 元素,例如:

var myDiv = $myDiv.get(0); // 获取第一个元素对应的 DOM 元素
myDiv.style.backgroundColor = 'red'; // 修改背景色

二、链式调用

        jQuery对象之所以可以链式调用是因为jQuery库中的每个方法都会返回自身的引用(即this),这样就可以一直链式调用下去,形成一个连贯的操作链。例如:

// 假设有一个id为myDiv的div元素
$('#myDiv')
    .addClass('highlight')
    .text('Hello, world!')
    .fadeIn(1000);

        在这个例子中,$('#myDiv')选择了id为myDiv的div元素并返回了一个jQuery对象。之后,.addClass('highlight')方法将为该对象添加一个CSS类highlight,并返回自身的引用。.text('Hello, world!')方法会将该元素的文本内容设置为'Hello, world!',并返回自身的引用。最后,.fadeIn(1000)方法将使该元素以1000毫秒的时间渐变出现,同时也返回自身的引用。因此,整个操作链都是由同一个jQuery对象执行的。

第十八章、DOM元素对象

        DOM(Document Object Model)元素对象指的是在HTML或XML文档中的所有元素,例如文本、图像、链接等等。在浏览器中,每一个HTML或XML元素都是一个DOM元素对象,可以通过JavaScript来访问和操作。DOM元素对象具有一系列的属性和方法,可以用来获取和设置元素的属性和内容,以及添加和移除元素等等。通常可以使用DOM API和jQuery等库来操作DOM元素对象。在Cypress中,也可以通过一些命令和函数来操作DOM元素对象,例如cy.get()、cy.contains()等。

一、DOM元素,DOM元素对象,DOM对象之间的区别和联系

        DOM(Document Object Model)元素指的是HTML或XML文档中的元素,如 <div><p><img> 等标签。这些元素可以通过JavaScript代码来访问和操作,比如修改元素的样式、内容或属性等。在网页加载后,浏览器会将HTML或XML文档解析成DOM元素,形成一个树形结构,我们可以通过DOM API来访问和操作这些元素。

<div id="myDiv">Hello World!</div>
  • DOM 对象:在 JavaScript 中,DOM 对象是代表整个文档的对象,通过该对象可以访问和操作文档中的任何元素。在这个例子中,使用 document 对象可以获取到整个文档对象,使用 document.getElementById("myDiv") 方法可以获取到 idmyDiv 的元素对象。

  • DOM 元素:DOM 元素是文档中的一个独立单元,可以是一个标签、一个属性、一个文本等等。在这个例子中,<div> 就是一个 DOM 元素。

  • DOM 元素对象:DOM 元素对象是通过 DOM 对象获取到的具体元素,可以对其进行操作或获取属性值。在这个例子中,通过 document.getElementById("myDiv") 获取到的就是 idmyDiv 的 DOM 元素对象,可以使用该对象的属性或方法进行操作,例如 myDiv.innerText = "Hello Cypress!" 可以将文本修改为 "Hello Cypress!"。

        因此,DOM 对象代表整个文档,DOM 元素是文档中的一个独立单元,DOM 元素对象是通过 DOM 对象获取到的具体元素对象。

二、DOM元素,jQuery对象,cypress命令对象之间的区别和联系

        DOM元素是指浏览器加载HTML文档后在内存中创建的对象,它们表示文档中的不同元素,如div、span、input等。可以通过JavaScript代码访问和操作DOM元素的属性和方法,例如设置元素的内容、添加/删除类名、绑定事件等。

        jQuery对象是一个封装了DOM元素的集合,它包含了一个或多个DOM元素,可以用jQuery提供的方法对其进行操作。jQuery提供了一系列的方法,使得操作DOM元素更加方便。例如,可以使用jQuery的选择器选取文档中的元素,使用jQuery提供的css()方法设置元素的样式,使用text()方法设置元素的文本内容等。

        Cypress命令对象是Cypress框架提供的对象,用于对Web应用程序进行端到端的测试。Cypress命令对象提供了一系列的方法,可以模拟用户在浏览器中的交互行为,例如点击、输入、选择、提交表单等。Cypress命令对象还提供了一系列的断言方法,用于检查Web应用程序的状态是否符合预期。

        在Cypress测试中,我们通常会使用Cypress命令对象来访问和操作DOM元素。可以使用Cypress提供的命令访问DOM元素,例如使用cy.get()方法选取元素,使用cy.type()方法模拟输入等。此外,Cypress也内置了jQuery库,可以直接使用$()方法来访问和操作DOM元素。同时,Cypress也提供了一些方法,用于将DOM元素和jQuery对象转换为Cypress命令对象,例如cy.wrap()方法和cy.wrap().as()方法。

        总的来说,浏览器的DOM元素、jQuery对象和Cypress命令对象是用于操作Web应用程序的不同对象,它们之间有着紧密的联系和相互转换的能力。在编写Cypress测试时,我们通常会使用这三种对象的组合来完成测试任务。

三、cypress中直接操作DOM元素

        在 Cypress 中,cy.get() 获取的确实是 Cypress 包装的 jQuery 对象,而不是原生的 DOM 对象。如果你需要直接操作原生的 DOM 对象,你可以使用 .then() 命令来获取原生的 DOM 元素,并进行操作。

例如,假设你想通过类名获取一个元素,并直接操作它的原生 DOM 对象,可以按照以下方式编写代码:

cy.get('.my-element').then(($element) => {
  // 获取原生 DOM 对象
  const domElement = $element[0];
  
  // 对原生 DOM 对象执行操作
  domElement.classList.add('new-class');
  domElement.textContent = 'Updated text';
  
  // 执行其他操作
});

        在上面的示例中,通过 cy.get('.my-element') 获取到 Cypress 的 jQuery 对象 $element,然后使用 .then() 命令访问回调函数,并在回调函数中通过 $element[0] 获取到原生的 DOM 对象 domElement。之后,你可以对 domElement 进行任何原生的 DOM 操作。

        通过这种方式,你可以在 Cypress 中同时享受 Cypress 的命令和断言功能,以及直接操作原生的 DOM 对象。

        在 Cypress 中,可以通过 .then() 回调函数来访问原生 DOM 元素。在回调函数中,将 $button 参数作为 Cypress 封装的 jQuery 对象传递给回调函数。然后,您可以使用 $button[0]$button.get(0) 来访问原生 DOM 元素,并对其执行操作,如点击。

以下是一个示例:

cy.get('.home-menu-trigger').then(($button) => {
  $button[0].click(); // 或者 $button.get(0).click();
});

        请注意,访问原生 DOM 元素可能会绕过 Cypress 的事件模拟和自动等待功能,因此需要确保元素在可见、可交互的状态下才进行点击操作。

第十九章、链式调用原理

 一、链式调用

cypress中一般只是会用到jQuery 对象,不会用到底层的DOM元素对象

        在 Cypress 中,方法返回的是一个 jQuery 对象,而 Cypress 命令本身是基于 jQuery 对象的一组自定义命令。所以可以通过 jQuery 的链式调用方式来进行多个 Cypress 命令的串联。

  例如:.click() 命令实际上是 Cypress 自定义的命令,不是 jQuery 对象的方法。它的作用是模拟用户点击操作,并触发相关事件。但是由于它返回的是与 .get() 方法返回值相同的 jQuery 对象,所以可以像 jQuery 方法一样进行链式调用。

二、subject主体

        subject在Cypress中代表当前正在操作的对象,可以是DOM元素、jQuery对象、Promise对象、Cypress命令对象、数组或数字等。

        在 Cypress 中,一个命令的参数 subject 指的是当前命令执行的主体,即前一个命令的返回值或者在当前命令中指定的主体subject 在命令链中传递,作为一个命令的输入,以便执行后续的命令。例如,对于以下代码:

cy.get('.my-class')
  .contains('Hello')
  .click()

        在这个命令链中,.get('.my-class') 返回的元素是 subject,它被传递给后续的命令,包括 .contains('Hello').click().contains('Hello') 命令使用了 subject,从而在选定元素中查找文本 "Hello"。.click() 命令也使用了 subject,从而在选定元素上执行了点击操作。

        在自定义命令中,也可以使用 subject 参数来表示前一个命令的返回值,以便在自定义命令中继续操作该元素。

三、subject主体内容

在Cypress中,subject还可以是以下几种类型:

  1. Cypress命令对象:前一个命令的返回值可以作为后一个命令的subject,这样就可以链式调用多个Cypress命令。

  2. 数组或JQuery对象数组:可以在一组元素上执行相同的Cypress命令。

  3. jQuery对象

  4. DOM元素

  5. 字符串或数字:可以作为某些Cypress命令的参数。

  6. Promise对象

        在Cypress中,链式调用中的 subject 可以是 DOM 元素本身,也可以是 jQuery 对象,还可以是 Promise 对象。这是因为 Cypress 的命令都是针对 DOM 元素进行操作的,而 Cypress 对象可以自动将不同的对象转换为 Cypress 命令对象,以便使用 Cypress 的命令进行操作。所以,不论 subject 是什么类型的对象,只要它是 Cypress 支持的对象类型之一,就可以在链式调用中使用。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Web端自动化测试框架是一种用于自动化测试Web应用程序的工具或框架。它可以模拟用户在浏览器中的操作,如点击、输入、提交表单等,并验证应用程序的行为是否符合预期。 常见的Web端自动化测试框架有以下几种: 1. Selenium:Selenium是一个广泛使用的Web自动化测试框架,支持多种编程语言,如Java、Python、C#等。它可以模拟用户在浏览器中的操作,并提供丰富的API用于验证页面元素、执行断言等。 2. Puppeteer:Puppeteer是由Google开发的一个Node.js库,它提供了一套API用于控制Chrome或Chromium浏览器。Puppeteer可以模拟用户在浏览器中的操作,并提供了丰富的功能,如截图、生成PDF等。 3. CypressCypress是一个现代化的前端测试工具,它可以直接在浏览器中运行测试代码,无需依赖第三方驱动程序。Cypress提供了简洁的API和强大的调试能力,可以轻松编写和运行自动化测试。 4. TestCafe:TestCafe是一个跨浏览器的自动化测试框架,它可以在真实的浏览器中运行测试用例。TestCafe支持多种编程语言,如JavaScript、TypeScript等,提供了简单易用的API和丰富的断言功能。 5. WebDriverIO:WebDriverIO是一个基于WebDriver协议的自动化测试框架,它支持多种浏览器和平台,并提供了丰富的API和插件生态系统。WebDriverIO可以与其他测试框架集成,如Mocha、Jasmine等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值