V8 引擎使得前端的工程化成为可能,前端的被细分到组件。这里将演示使用 Cypress 对组件进行测试
Cypress(官网),是一个自动测试套件,可以让用户可视化地进行端对端集成测试,或者是组件测试。与 jest + testing-library 这种注重单测的组合不同,Cypress 提供更加丰富的界面交互。
当我们开发组件库 (Components) 而不是应用 (Application) 的时候,往往没有一个可以打开的页面让我们查看到组件的样子,不像依赖于构建工具,像 Webpack、Vite 等,提供了方便的实时功能,组件库的开发不需要一个 index.html
文件。那么使用 Cypress 这样的可视化测试套件就是很好的选择。
组件库伊始
从头创建一个简单的组件库,一开始可能是像这样的简单的目录结构:
- src
- index.css
- index.tsx
- package.json
- tsconfig.json
因为是 React-ts 项目,所以这里有 tsconfig.json
。package.json
里也只有简单的 React 依赖:
{
// ...
"dependencies": {
"react": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.45"
}
}
还有 index.tsx
和 index.css
内容如下,它的效果是一个类似于苹果窗口:
但是在我们编写的代码的时候怎么才能看到组件的效果呢?(;´д`)ゞ
当然可以用 vite 直接构建一个应用,将组件塞到应用里展示。但是这样却更像是开发单页应用:
- 你能够看到目录下的
index.html
文件是组件完全无关的功能 - 使用 vite build 后的程序入口
dist/index.html
也不是我们的组件入口 - 如果将你的组件库发布到 npm 上,也多了很多组件外的代码
而 Cypress 可以作为组件的测试、可视化工具,可以让你的项目目录干净很多:
打包后的 dist
目录十分干净,index.js
就是组件的入口,当然这需要打包工具的配合,我使用的是 rollup。
那赶紧端上来罢~(恼)
安装 Cypess
在项目根目录下运行命令 npm i -D cypress
在 package.json
配置测试命令脚本:
"scripts": {
"test": "cypress open"
}
第一次的配置
然后运行 npm test
一阵鬼畜之后会打开一个窗口:
这是是让你选择要 E2E 测试还是 组件测试,我们当然是组件测试。因为这里是我们在项目里第一次使用 Cypress 测试,可以看到两个测试类别下面都有 Not Configured
的标识。
点击 Component Testing 后会让你进行一些配置,首先是前端框架和打包工具的选择:
这里使用的是 React 框架,打包工具我选择的是 Vite。然后点击 Next Step,下一步需要你安装对应的包,这里会罗列出需要你安装的包和接受的版本,已经安装好的在右侧是一个打勾的状态,顶部还贴心地提供了安装的命令,但是这里要注意的是,如果直接使用它提供的命令,可能会有版本不符的情况。比如我现在写这篇的文章的时候,Vite 的最新版是 5.x,如果直接使用它提供的命令:npm install -D vite react-dom
将安装最新的 5.x 的 Vite,但是列表里需要的版本并不包括 5.x,所以要特别注意,安装 Vite 的时候使用 npm i -D vite@4.0.0
。
在安装好依赖后所有的列表右侧会变成打勾状态,左下角的按钮会变成 Continue,点击继续,
这里是 Cypress 帮我们添加好了配置文件,点击 Continue 继续,然后会看到这个东西:
这是因为虽然我们指定了 Vite 为打包工具,但是我们的目录下并没有 vite.config.ts
或相关的配置文件,所以先去弄一个。
在根目录下 vite.config.ts
,内容如下:
import { defineConfig } from 'vite'
export default defineConfig({
})
然后回到 Cypress 的窗口,点击上面的 Try again,这次它会检测到有了 vite.config.ts
,并出现这个界面,要我们选择展示组件的浏览器:
选一个你喜欢的就好。然后会弹出另一个测试界面:
此时我们还没有创建任何的测试,点击 Create from component,Cypress 会检测我们的组件,选择一个开始测试:
它帮我们生成了一些默认的测试代码:
这样就好,点击 Okay,在出现的界面中就能够看到我们组件的样子了,
左侧是组件列表,右侧是我们选中的组件的样式。回到我们的代码里,在我们的组件的同目录中会出现一个测试文件:indexAppleBox.cy.tsx
,我们对当前组件的测试就写在这里了:
初始代码的问题
上面的图里的代码是 Cypress 给我们默认生成的测试代码,你能够看到在 describe、it、cy、AppleBox 下分别有四条红色波浪线,describe、it、cy 的红色波浪线是因为 ts 编译器找不到这几个变量的定义,AppleBox 的是因为没有传递给组件必传的参数: Content。Cypress 也不是面面俱到的。
修复 AppleBox 的容易,随便传递一个值给 Content 就可以;describe、it、cy 定义的缺失需要在 tsconfig.json
的 include 节点添加 "cypress",在 compilerOptions -> types 节点添加 "cypress",然后在当前组件的测试文件 indexAppleBox.cy.tsx
中 添加引用 import { mount } from 'cypress/react18'
,并将 cy.mount
改成 mount
即可,同时,如果 tsconfig.json
有提示类似的警告:Cannot write file '*********/cypress/support/commands.js' because it would overwrite input file.
,就在 compilerOptions
节点下添加 "outDir": "dist"
(更详细的看这里:typescript-error-cannot-write-file-because-it-would-overwrite-input-file)。改完后的就像这样:
{
"compilerOptions": {
"outDir": "dist",
...
"types": ["cypress", "node"]
},
"include": ["src/**/*", "cypress"]
...
}
// indexAppleBox.cy.tsx
import React from 'react'
import { AppleBox } from './index'
import { mount } from 'cypress/react18'
describe('<AppleBox />', () => {
it('renders', () => {
// see: https://on.cypress.io/mounting-react
mount(<AppleBox content='CONTENT'/>)
})
})
上面的问题虽然不修复 Cypress 也可以跑,但是对于我们后续的打包会有影响,还是修复了好。
查询和断言
当然现在简陋的测试代码并不能算上是测试,只是将组件渲染出来而已,Cypress 也提供了查询组件、断言的 API。当使用 mount 加载了组件后,要使用查询 API 查找组件,通过使用断言 API 检查组件是否按照预期中运行。 这个部分就很形象 jest + testing-library 的测试库了,Cypress 也提供了这样的功能,十分强大!
查询
在组件挂在后,需要查询获取到这个组件,在 Cypress 里提供了丰富的查询 API,常用的是 cy.get()
,它接受类似 CSS 选择器的语法,比如 cy.get('.container')
查询类名包含 container
的元素;cy.get('[data-cy="container"]')
查询属性名 data-cy
的值为 container
的元素。更多丰富的查询 API 看官方的文档。
获取到元素之后就要使用断言检查。
断言
Cypress 的断言库使用的是 Chai,提供了丰富的断言功能,Cypress 断言文档。 断言 API 可以在查询 API 之后链式调用,如使用 should() 断言组件存在。
cy.get('[data-cy="container"]').should('exist');
然后在 Cypress 窗口里查看,能看到左侧执行了 mount、get、assert,在断言部分成功的,能看到是绿色高亮的。
如果将测试代码改成这样 cy.get('[data-cy="container"]').should('not.exist');
,它是断言失败的,会用红色高亮,并给出详细信息:
更详细的 API 在文档里,太多了不想写。
最佳实践
Cypress 特别详细说明了使用查询和断言的最佳实践,Best Practices。 如:
- 不要使用变量来存放查询到的组件,这样就无法获取到组件的最新状态,要每次都使用查询 API 获取组件。
- 不要使用查询元素、类名、ID来获取组件,组件的类名、ID 可能非常容易改变,会使你的测试用例不稳定。要在组件上添加属性
data-cy
,使用cy.get('[data-cy="xxx"]')
来查询组件。
等等非常多的用例,详细查看文档。
总结
Cypress 对于 E2E 的测试和组件的测试都是非常便利的。
这篇博客非常详细地介绍了如何使用 Cypress 进行组件测试,从安装 Cypress,到配置,再到实际的测试过程,作者都进行了详尽的阐述,使得读者可以很容易地理解和跟随。这篇博客的优点在于,作者不仅提供了代码示例,还包含了大量的截图,使得教程更加直观易懂。此外,作者还提供了相关的链接,方便读者进一步学习和探索。
博客的核心理念是鼓励使用 Cypress 进行组件测试,这一点我非常赞同。Cypress 是一个强大的测试工具,它可以帮助开发者确保他们的代码在各种情况下都能正常工作。在这个快速发展的技术世界中,保证代码的稳定性和可靠性是至关重要的。
然而,博客的一些部分可以进行改进。例如,虽然作者提供了大量的代码示例,但在某些情况下,他没有充分解释这些代码的工作原理。对于那些不熟悉 Cypress 或 JavaScript 的读者来说,这可能会造成一些困扰。因此,我建议作者在提供代码示例时,应更详细地解释每一行代码的功能。
此外,我注意到在博文中有一些错别字和语法错误,这可能会对读者理解文章内容造成一些困扰。因此,我建议作者在发布博客之前,应进行更仔细的校对。
总的来说,这是一篇非常有用的博客,对于那些想要学习如何使用 Cypress 进行组件测试的开发者来说,这将是一个宝贵的资源。我期待看到作者在未来的博客中,继续分享他的知识和经验。