编写和组织测试
学习内容
- 如何在Cypress中组织测试及支持的文件类型
- 如何编写Cypress测试(含钩子函数、排除项和配置)
- 测试状态及其解读方法
- 编写Cypress测试时自动监视的文件
文件夹结构
添加新项目后,Cypress会自动生成推荐的文件夹结构。默认创建如下:
- JavaScript
- TypeScript
端到端测试:
/cypress.config.js
/cypress/fixtures/example.json
/cypress/support/commands.js
/cypress/support/e2e.js
组件测试:
/cypress.config.js
/cypress/fixtures/example.json
/cypress/support/commands.js
/cypress/support/component.js
/cypress/support/component-index.html
两者兼备:
/cypress.config.js
/cypress/fixtures/example.json
/cypress/support/commands.js
/cypress/support/e2e.js
/cypress/support/component.js
/cypress/support/component-index.html
端到端测试:
/cypress.config.ts
/cypress/fixtures/example.json
/cypress/support/commands.ts
/cypress/support/e2e.ts
组件测试:
/cypress.config.ts
/cypress/fixtures/example.json
/cypress/support/commands.ts
/cypress/support/component.ts
/cypress/support/component-index.html
两者兼备:
/cypress.config.ts
/cypress/fixtures/example.json
/cypress/support/commands.ts
/cypress/support/e2e.ts
/cypress/support/component.ts
/cypress/support/component-index.html
配置文件夹结构
虽然Cypress允许配置测试、夹具和支持文件的存放位置,但如果是首次创建项目,我们建议使用上述结构。
可以通过配置文件修改文件夹配置。详见Cypress配置。
Cypress会创建screenshotsFolder
和videosFolder
来存储测试过程中生成的截图和视频。建议将这些文件夹加入.gitignore。如果配置文件中包含敏感环境变量或使用了cypress.env.json
,也应忽略这些文件。
测试规范文件
测试文件默认位于cypress/e2e
,但可通过配置修改路径。支持以下格式:
.js
.jsx
.ts
.tsx
.coffee
.cjsx
Cypress原生支持ES2015
,可使用ES2015模块
或CommonJS模块
,支持import
和require
引入npm包和本地模块。
查看我们使用ES2015和CommonJS模块的示例。
要查看所有Cypress命令的示例,请打开cypress/e2e
文件夹中的2-advanced-examples
文件夹。
夹具文件
夹具文件是测试使用的静态数据,默认位于cypress/fixtures
,可通过配置修改路径。通常与cy.fixture()
命令配合使用,常用于模拟网络请求。
资源文件
测试运行后会生成一些包含资源的文件夹,建议将这些文件夹加入.gitignore。
下载文件
测试文件下载功能时下载的文件默认存储在downloadsFolder
(默认为cypress/downloads
)。
/cypress
/downloads
- records.csv
截图文件
通过cy.screenshot()命令或测试失败时自动生成的截图默认存储在screenshotsFolder
(默认为cypress/screenshots
)。
/cypress
/screenshots
/app.cy.js
- Navigates to main menu (failures).png
更多截图设置请参阅截图和视频。
视频文件
测试运行的录像默认存储在videosFolder
(默认为cypress/videos
)。
/cypress
/videos
- app.cy.js.mp4
资源文件路径
生成的截图和视频会保存在各自文件夹中。文件路径会根据specPattern
选项(或通过--spec
命令行选项或spec
模块API选项)去除所有规范文件共享的公共祖先路径。
示例1:
- 规范文件:
cypress/e2e/path/to/file/one.cy.js
- 公共祖先路径:
cypress/e2e/path/to/file
- 生成截图:
cypress/screenshots/one.cy.js/your-screenshot.png
- 生成视频:
cypress/videos/one.cy.js.mp4
示例2:
- 规范文件:
cypress/e2e/path/to/file/one.cy.js
cypress/e2e/path/to/two.cy.js
- 公共祖先路径:
cypress/e2e/path/to/
- 生成截图:
cypress/screenshots/file/one.cy.js/your-screenshot.png
cypress/screenshots/two.cy.js/your-screenshot.png
- 生成视频:
cypress/videos/file/one.cy.js.mp4
cypress/videos/two.cy.js.mp4
Cypress云中的资源

无需手动管理资源,可通过Cypress云保存。使用 Cypress测试回放功能可以回放测试执行过程。
截图和视频会永久存储,附加到相应的测试结果中,便于通过网页界面浏览或分享。更多视频设置请参阅截图和视频。
插件文件
插件文件是一个特殊的Node文件,在项目加载前、浏览器启动前和测试执行期间运行。虽然Cypress测试在浏览器中执行,但插件文件在后台Node进程中运行,使测试能够通过cy.task()命令访问文件系统和操作系统。
插件文件适合定义如何通过预处理器打包规范文件,如何通过浏览器启动API查找和启动浏览器等功能。详见插件指南。
初始导入的插件文件可配置为其他文件。
支持文件
要包含在测试文件之前运行的代码,可设置supportFile
路径。默认查找以下文件:
组件测试:
cypress/support/component.js
cypress/support/component.jsx
cypress/support/component.ts
cypress/support/component.tsx
端到端测试:
cypress/support/e2e.js
cypress/support/e2e.jsx
cypress/support/e2e.ts
cypress/support/e2e.tsx
对于给定的测试类型,存在多个匹配的supportFile
文件会导致Cypress加载时报错。
Cypress会为每个配置的测试类型自动创建示例支持文件,其中包含多个注释掉的示例。
该文件在每个规范文件之前运行,作为便捷机制避免手动导入。默认情况下,Cypress会自动包含特定类型的支持文件。
支持文件适合放置可重用行为,如自定义命令或要应用于所有规范文件的全局覆盖。初始导入的支持文件可配置为其他文件或完全关闭,使用supportFile配置。可从支持文件中import
或require
其他文件以保持组织有序。
可在任何cypress/support
文件中定义before
或beforeEach
行为:
beforeEach(() => {
cy.log('我在每个规范文件的每个测试前运行!!!!!!')
})

注意: 此示例假设您已熟悉Mocha钩子。
执行顺序
Cypress在规范文件之前执行支持文件。例如:
端到端测试示例:
support/e2e.js
(支持文件)e2e/spec-a.cy.js
(规范文件)
组件测试示例:
support/component.js
(支持文件)components/Button/Button.cy.js
(规范文件)
故障排查
如果Cypress找不到规范文件,可通过启用调试日志进行排查:
DEBUG=cypress:cli,cypress:data-context:sources:FileDataSource,cypress:data-context:sources:ProjectDataSource npx cypress open
## 或
DEBUG=cypress:cli,cypress:data-context:sources:FileDataSource,cypress:data-context:sources:ProjectDataSource npx cypress run
编写测试
Cypress基于Mocha和Chai构建,支持Chai的BDD
和TDD
断言风格。编写的测试大多遵循这种风格。
如果您熟悉JavaScript测试编写,那么编写Cypress测试将非常简单。
需要低代码方式创建测试?使用Cypress Studio记录浏览器交互。
测试结构
测试接口借鉴Mocha,提供describe()
、context()
、it()
和specify()
。
context()
等同于describe()
,specify()
等同于it()
,可根据喜好选择术语。
// -- 应用代码 --
function add(a, b) {
return a + b
}
function subtract(a, b) {
return a - b
}
function divide(a, b) {
return a / b
}
function multiply(a, b) {
return a * b
}
// -- 应用代码结束 --
// -- Cypress测试 --
describe('数学函数单元测试', () => {
context('数学运算', () => {
it('可以相加数字', () => {
expect(add(1, 2)).to.eq(3)
})
it('可以相减数字', () => {
expect(subtract(5, 12)).to.eq(-7)
})
specify('可以相除数字', () => {
expect(divide(27, 9)).to.eq(3)
})
specify('可以相乘数字', () => {
expect(multiply(5, 4)).to.eq(20)
})
})
})
// -- 测试结束 --
钩子函数
Cypress还提供钩子函数(借鉴自Mocha)。
这些钩子有助于在测试集之前或每个测试之前设置条件,也便于在测试集之后或每个测试之后清理条件。
before(() => {
// 根级钩子
// 所有 测试前运行一次
})
beforeEach(() => {
// 根级钩子
// 每个测试块前运行
})
afterEach(() => {
// 每个测试块后运行
})
after(() => {
// 所有测试完成后运行一次
})
describe('钩子函数', () => {
before(() => {
// 该区块所有测试前运行一次
})
beforeEach(() => {
// 该区块每个测试前运行
})
afterEach(() => {
// 该区块每个测试后运行
})
after(() => {
// 该区块所有测试后运行一次
})
})
钩子和测试执行顺序:
- 所有
before()
钩子运行(一次) - 任何
beforeEach()
钩子运行 - 测试运行
- 任何
afterEach()
钩子运行 - 所有
after()
钩子运行(一次)
🚨 在编写after()
或afterEach()
钩子前,请阅读关于使用after()或afterEach()清理状态的反模式思考。
排除和包含测试
要运行特定套件或测试,在函数后追加.only
。嵌套套件也会执行。这使我们能够一次运行一个测试,是推荐的测试套件编写方式。
// -- 应用代码 --
function fizzbuzz(num) {
if (num % 3 === 0 && num % 5 === 0) {
return 'fizzbuzz'
}
if (num % 3 === 0) {
return 'fizz'
}
if (num % 5 === 0) {
return 'buzz'
}
}
// -- 应用代码结束 --
// -- Cypress测试 --
describe('FizzBuzz单元测试', () => {
function numsExpectedToEq(arr, expected) {
arr.forEach((num) => {
expect(fizzbuzz(num)).to.eq(expected)
})
}
// 仅运行此测试
it.only('当数字是3的倍数时返回"fizz"', () => {
numsExpectedToEq([9, 12, 18], 'fizz')
})
it('当数字是5的倍数时返回"buzz"', () => {
numsExpectedToEq([10, 20, 25], 'buzz')
})
it('当数字是3和5的倍数时返回"fizzbuzz"', () => {
numsExpectedToEq([15, 30, 60], 'fizzbuzz')
})
})
要跳过特定套件或测试,在函数后追加.skip()
。嵌套套件也会跳过。
it.skip('当数字是3的倍数时返回"fizz"', () => {
numsExpectedToEq([9, 12, 18], 'fizz')
})
测试隔离
最佳实践: 测试应始终能够独立运行且仍能通过。
正如我们的使命所述,我们致力于推广真正有效的测试流程,并构建Cypress以引导开发人员从一开始就编写独立测试。
我们通过在每次测试前清理测试状态和浏览器上下文来确保一个测试的操作不会影响后续测试。每个测试的目标应该是可靠地通过,无论是独立运行还是与其他测试连续运行。依赖于先前测试状态的测试可能导致不确定的测试失败,增加调试难度。
在干净的浏览器上下文中运行测试的行为称为testIsolation
。
测试隔离是全局配置,可通过testIsolation
选项在端到端测试的describe
级别覆盖。
要了解更多关于此行为及禁用它的权衡,请参阅测试隔离指南。
测试配置
可以为套件或测试应用测试配置值。将配置对象作为第二个参数传递给测试或套件函数。
这些配置将在设置它们的套件或测试期间生效,完成后恢复为之前的默认值。
语法
describe(name, config, fn)
context(name, config, fn)
it(name, config, fn)
specify(name, config, fn)
允许的配置值
注意: 某些配置值是只读的,无法通过测试配置更改。请查看测试配置选项列表。
套件配置
如果想针对特定浏览器运行或排除一组测试,可以在套件配置中覆盖browser
配置。browser
选项接受与Cypress.isBrowser()相同的参数。
以下套件测试在Chrome浏览器中运行时会被跳过。
describe('非Chrome环境', { browser: '!chrome' }, () => {
it('显示警告', () => {
cy.get('[data-testid="browser-warning"]').should(
'contain',
'为了最佳浏览体验,请使用Chrome浏览器'
)
})
it('链接到浏览器兼容性文档', () => {
cy.get('a.browser-compat')
.should('have.attr', 'href')
.and('include', 'browser-compatibility')
})
})
以下套件测试仅在Firefox浏览器中执行。其中一个测试会覆盖视口分辨率,并将当前环境变量与提供的环境变量合并。
describe(
'Firefox环境',
{
browser: 'firefox',
viewportWidth: 1024,
viewportHeight: 700,
env: {
DEMO: true,
API: 'http://localhost:9000',
},
},
() => {
it('设置预期视口和API URL', () => {
expect(cy.config('viewportWidth')).to.equal(1024)
expect(cy.config('viewportHeight')).to.equal(700)
expect(cy.env('API')).to.equal('http://localhost:9000')
})
it(
'使用最近的API环境变量',
{
env: {
API: 'http://localhost:3003',
},
},
() => {
expect(cy.env('API')).to.equal('http://localhost:3003')
// 其他环境变量保持不变
expect(cy.env('DEMO')).to.be.true
}
)
}
)
单个测试配置
可以配置cypress run
或cypress open
期间的重试次数。详见测试重试。
it('未认证用户应重定向到登录页', {
retries: {
runMode: 3,
openMode: 2
}
} () => {
// 测试代码...
})
})
动态生成测试
可以使用JavaScript动态生成测试。
describe('如果应用使用jQuery', () => {
;['mouseover', 'mouseout', 'mouseenter', 'mouseleave'].forEach((event) => {
it('触发事件: ' + event, () => {
// 如果应用使用jQuery,可以触发jQuery事件
cy.get('#with-jquery')
.invoke('trigger', event)
.get('[data-testid="messages"]')
.should('contain', '事件 ' + event + '已触发')
})
})
})
上述代码将生成包含4个测试的套件:
> 如果应用使用jQuery
> 触发事件: 'mouseover'
> 触发事件: 'mouseout'
> 触发事件: 'mouseenter'
> 触发事件: 'mouseleave'
断 言风格
Cypress支持BDD(expect
/should
)和TDD(assert
)风格的普通断言。阅读更多关于普通断言的内容。
it('可以相加数字', () => {
expect(add(1, 2)).to.eq(3)
})
it('可以相减数字', () => {
assert.equal(subtract(5, 12), -7, '这些数字相等')
})
.should()命令及其别名.and()也可用于更轻松地从Cypress命令链中断言。阅读更多关于断言的内容。
cy.wrap(add(1, 2)).should('equal', 3)