对应系统版本,下载并安装 Node.js (nodejs.org)
查看当前源
npm config get registry
若源url为境外地址,更新为国内镜像
npm config set registry https://registry.npm.taobao.org/
cd /[项目根目录]
如果项目根目录下缺少 node_modules 文件夹或者 package.json 文件,需先执行npm初始化
npm init
--save-dev 命令使Cypress作为dev dependency被安装,也可简写为 -D
npm install cypress --save-dev
成功安装后项目根目录下会生成 node_modules 文件夹,并包含可执行文件 ./node_modules/.bin
若遇到报错
The Cypress App could not be downloaded.
URL: https://download.cypress.io/desktop/10.1.0?platform=win32&arch=x64
Error: connect ETIMEDOUT 172.67.69.12:443
这个主要因为内网dhwifi网络做了屏蔽,则把网络切换为自己的移动wifi即可。
此步骤适用于VS Code,Powershell;若使用Git Bash终端或命令行(CommandLine),可跳过该步骤
查看当前命令终端权限
get-executionpolicy
如果显示为【限制】 Restricted ,则需要设置授权,执行
set-ExecutionPolicy RemoteSigned
之后再次查看权限,若显示为【远程签署】 RemoteSigned ,则表示授权成功
注意:若是对本地项目进行测试时,启动Cypress前需确保所在项目的网页APP已启动
./node_modules/.bin/cypress open
$(npm bin)/cypress open
注意:npx需要安装的npm版本 > 5.2
npx cypress open
若启动命令时,遇到错误
No version of Cypress is installed in: C:\..\Cypress\Cache\x.x.x\Cypress
则需要手动安装Cypress执行文件
npx cypress install
而后,再使用启动命令
成功启动Cypress后,会弹出Cypress的测试器窗口,如下。该测试器会自动识别该机器上与Cypress兼容的浏览器,并可通过右上角下拉式菜单选择。
测试器正下方【INTEGRATION TESTS】为测试示例,可以依次点击运行。
测试文件需要添加在项目文件夹下的 /cypress/integration/ 文件夹中,可以通过新建文件夹归档。
测试文件为JavaScript类型,例 samepleTest.js 。添加文件后,可以注意到在Cypress测试器中对应测试 samepleTest.js 的增加。
点击该测试查看细节(因为暂无测试用例,当前会显示找不到测试 No tests found. )。
在 samepleTest.js 中添加如下内容并保存,以查看Cypress如何展示测试是否成功
describe('成功的测试', () => {
it('TRUE==TRUE', () => {
expect(true).to.equal(true)
})
})
describe('失败的测试', () => {
it('TRUE!=FALSE', () => {
expect(true).to.equal(false)
})
})
在打开Cypress的测试器时,可以更改测试文件,保存后的更改内容会实时反馈在测试器中,以便调试。
一般而言,测试由3个部分构成
设定当前状态
执行某个操作
断言状态变更
describe('My First Test', () => {
it('Gets, types and asserts', () => {
// Given: 访问页面
cy.visit('https://example.cypress.io')
// When: 点击内容包含‘type’的元素
cy.contains('type').click()
// Then:获取URL,其中应包含‘/commands/actions’
cy.url().should('include', '/commands/actions')
// Given:找到class为‘action-email’的元素
// When:输入‘[email protected]’
// Then:该元素的value应是‘[email protected]’
cy.get('.action-email')
.type('[email protected]')
.should('have.value', '[email protected]')
})
})
注意:
(1)Cypress使用链式语法
(2)单个测试 describe('测试',()=>{}) 中可以包含多个测试用例 it('测试用例',()=>{}) ;单个测试用例中可以包含多个平行的或顺序的断言 xxx.should()
(3)可以通过链式语句增加状态、动作和断言
(4)当步骤涉及到页面加载时,Cypress会等待加载完毕才会继续执行后续步骤
Cypress提供了一系列的debugging工具,以帮助理解测试。
在Cypress测试器中,将鼠标移动到测试用例中任意行命令并悬停,Cypress会回溯到该命令执行时的状态,在右侧界面显示当时的页面(注意截图中的URL为页面跳转前的地址),高亮并置顶相关DOM组件。
在Cypress测试器中,单击任意行命令可以将其固定(以紫色高亮),这么做可以:
当命令被固定后,鼠标移动到其他命令上时,右侧界面不会再变化。这样可以手动查看截图时页面的DOM元素。
因为 .click() 是一个动作命令,该命令被固定时,可以观察到右侧界面的 type 处有红点显示(代表此处有事件触发)
当被锁定的命令是动作命令时(如截图中的 .type() ),右侧界面下方会出现额外的截图菜单,通过选择 before 或 after 来查看该动作命令执行前后的页面区别。
注意:如果动作命令涉及到页面加载(非瞬时操作),取决于该页面的加载速度,会导致选择 after 后可能看不到变化,或者只显示页面加载时的空白。
在测试中,当错误发生时,Cypress会提供以下信息:
显示错误的类型,如 CypressError , AssertionError
显示错误的具体信息。有时还会提供修复办法。
如果有此选项,点击此项可以打开相关的Cypress文档页面
显示报错代码所在文件名、行数、字数。点击此项可以使用默认编辑器打开该文件,并高亮报错行(高亮需要编辑器支持)。
显示报错代码及上下文,并高亮报错代码行。
打开或关闭改错误堆栈轨迹的开关。
点击此项可以把完整错误信息打印到DevTools的控制台中。
在Cypress测试器的左侧界面中,注意到有以下这样的日志——它们并不属于代码中的任何命令。这是因为CYpress会自动记录下测试中的重要页面事件。
Cypress会记录的页面事件有:
网络XHR的请求
URL hash的变更
页面的加载
表单的提交
Cypress可以在控制台中提供额外的调试信息。
打开浏览器的Dev Tools F12 ,并点击左侧界面中的 GET class选择器命令行。
可以看到Cypress提供的额外信息包括:
Command :所触发的命令
Yielded :命令返回的内容
Elements :找到的元素数量
Selector :所使用的参数
注意:若返回多个元素,可以在Dev Tools中展开以查看每个元素的信息。
Cypess也提供了调试用的特殊命令,如 cy.pause() , cy.debug() 。
尝试在代码中加入 cy.pause() ,如下:
describe('My First Test', () => {
it('Gets, types and asserts', () => {
// Given: 访问页面
cy.visit('https://example.cypress.io')
// 调试:特殊命令
cy.pause()
// When: 点击内容包含‘type’的元素
cy.contains('type').click()
// Then:获取URL,其中应包含‘/commands/actions’
cy.url().should('include', '/commands/actions')
// Given:找到class为‘action-email’的元素
// When:输入‘[email protected]’
// Then:该元素的value应是‘[email protected]’
cy.get('.action-email')
.type('[email protected]')
.should('have.value', '[email protected]')
})
})
如此,在Cypress测试器中,我们可以仿照debugger,在暂停处使用下一步 step forward 的调试功能
注意:不要从Cypress脚本启动服务器。详情参考Cypress的最佳实践。
在测试文件夹 cypress/integration 下,新建你的测试脚本 xxx.js
describe('应用首页', () => {
it('加载成功', () => {
cy.visit('http://localhost:8080') // URL替换为你的开发环境URL
})
})
保存后在Cypress测试器中打开该测试 xxx.js 。若一切顺利,测试器中可以看到已访问对应URL的页面。
考虑到每个测试都需要访问页面,Cypress提供了配置基础URL的方式:在配置文件中 cypress.json ,加上 baseUrl 的配置。
{
"baseUrl": "http://localhost:8080"
}
如此,在测试中调用 cy.visit() 和 cy.request() 命令时,会自动使用配置URL作为前缀。
配置完成后,先前的 xxx.js 可以写成这样:
describe('应用首页', () => {
it('加载成功', () => {
cy.visit('/')
})
})
测试哪些方面,如何定义测试用例的边界值,如何做回归测试,等等,完全取决于你自己、你的应用和你的团队,请自由发挥。
最常用的3个准备命令:
cy.exec() :用以执行系统指令
cy.task() :在Node中通过 pluginsFile 来执行命令
cy.request() :发出HTTP请求
例如,如果机器上装有 node.js ,那么可以在 before 或者 beforeEach 钩子代码中执行 npm 任务。
describe('应用首页', () => {
beforeEach(() => {
// 重置数据库并使用种子数据
cy.exec('npm run db:reset && npm run db:seed')
})
it('加载成功', () => {
cy.visit('/')
})
})
除了使用种子数据,Cypress也支持仿造返回的JSON,这与Mock相似。这么一来,不仅避免了数据从服务器到浏览器的同步等待时间,也避免了测试时的状态转变。这也意味着一个测试不会生成影响其他测试的状态。
此外,通过这种方法,可以完全无需后台服务器就建立一套完整的测试,并赋予想要准备的数据。
尽管如此,以上两种策略之间应该有个平衡点,需要自己去探索。
另一方面,使用Mock的话没办法保证仿造的数据与服务器的真实返回一致。幸运的是,Cypress提供了一些方法可以解决这个问题。
1. 基境数据
可以预先通过服务器生成的真实数据,并存为基境数据,用以Mock
2. E2E测试+Mock测试
这是一种更平衡的策略。可以先完成一个无Mock的端到端测试,再通过Mock去完成所有的边界测试和特殊情景测试。这样既保证了测试的真实性,同时也兼顾了测试用例的便捷性和完成速度。
登录测试往往是你的应用里第一个(甚至是最难的之一)需要征服的测试。基于上一部分的两种策略,可以在Cypress中这样实现:
1. 完整的登录流程只测试一次
因为登录是最重要的功能之一,并且涉及到与服务器的交互,所以这里推荐使用UI交互的方式去测试。
以下是一段代码示例,包含了种子数据策略:
describe('登录页面', () => {
beforeEach(() => {
// 重置数据库并使用种子数据
cy.exec('npm run db:reset && npm run db:seed')
// 为接下来的测试创建一个种子账号
// 假设它同时会生产一个随机密码
cy.request('POST', '/test/seed/user', { username: 'jane.lane' })
.its('body')
.as('currentUser')
})
it('通过登录页面登录并设置权限cookie', function () {
// 从this.currentUser中解构出username和password
const { username, password } = this.currentUser
cy.visit('/login')
// 从name=username的输入框输入刚刚获得的username
cy.get('input[name=username]').type(username)
// 从name=password的输入框输入刚刚获得的password
// {enter}模拟回车触发登录请求
cy.get('input[name=password]').type(`${password}{enter}`)
// 成功登录后,URL应当包含dashboard()
cy.url().should('include', '/dashboard')
// 权限cookie应当存在
cy.getCookie('your-session-cookie').should('exist')
// UI界面应当反映登录的用户名
cy.get('h1').should('contain', 'jane.lane')
})
})
类似的,完整的UI测试还可以覆盖如下:
用户名/密码为空
用户名/密码不正确
注册时用户名已存在
注册时密码强度不符合要求
特殊情况如账号被锁/被删除
2. 其他相关的测试使用Mock
当需要继续进行其他依赖于登录状态的测试时,则推绕开UI登录的方法。Cypress提供了 cy.request() 指令,它可以自动获取和设置cookies。我们可以通过这个指令来构建和通过UI登录完全一致的登录状态,而不需要借助UI。
示例代码如下:
describe('Dashboard页面', () => {
beforeEach(() => {
// 重置数据库并使用种子数据
cy.exec('npm run db:reset && npm run db:seed')
// 创建一个种子账号
// 假设它同时会生产一个随机密码
cy.request('POST', '/test/seed/user', { username: 'jane.lane' })
.its('body')
.as('currentUser')
})
it('绕过UI,自动登录', function () {
// 从this.currentUser中解构出username和password
const { username, password } = this.currentUser
// 通过POST请求自动登录,从而绕过UI
cy.request('POST', '/login', {
username,
password,
})
// 现在已经是登录状态
// 可以不受限制地访问任何页面了
cy.visit('/dashboard')
// 权限cookie应当存在
cy.getCookie('your-session-cookie').should('exist')
// UI界面应当反映登录的用户名
cy.get('h1').should('contain', 'jane.lane')
})
})
通过绕过UI的方法,对于后续的每个测试,我们都节约了大量的时间(加载登录页面,从UI输入用户名、密码再登录)。并且因为此前已经完整地测试过登录的流程,所以我们有十足的把握来用这一快捷方式。
一言以蔽之:
当需要测试特定功能时,应当用完整的UI测试
当需要准备某一状态(基境)且这个状态已经被完整测试过时,准备的过程可以通过绕过UI的方式来实现
关于Cypress的介绍已经步入尾声。从现在起,开始为你的应用测试吧!
Why Cypress? | Cypress Documentation
GitHub - cypress-io/eslint-plugin-cypress: An ESLint plugin for projects that use Cypress