此部分现已标记为过时,因为它指的是一个非常古老的 Cypress 版本(现在已完全支持组件测试)。
更新:Cypress 10 发布,集成了组件测试和 E2E 测试,请查看并忽略下面报告的所有配置步骤,因为它们已经过时了!
更新:Cypress 7 发布,支持全新的组件测试,快去看看吧!而且,还有其他令人兴奋的消息即将到来,感谢Storybook 6.2 发布!
如今,使用 Cypress 进行 React 组件的单元测试已经成为可能,这是 使用 Cypress 和 Storybook 进行组件测试 的一个扩展章节。
前一章的目标是在React 组件测试世界中进行一些实验,这是现今一个非常重要的主题。
动机非常简单:
-
你可能已经在你的团队中使用了 Storybook(如果没有,请考虑添加!)
-
你可能不熟悉使用 Testing Library 进行组件测试,或者你对 JSDom 有一些偏见,或者你希望在真实浏览器中测试你的 UI 组件,而不是在模拟的 DOM 环境中进行测试。
-
你可能已经熟悉 Cypress 或 TestCafé(如果没有,请考虑它们用于你的 UI 测试),并且你可能希望只使用一个工具进行测试。
方法也很简单:
-
将故事的 props 暴露给测试工具,用于控制渲染的组件。
-
从 Cypress/TestCafé 中获取它们,自动执行用户操作并断言 props 的内容。
但是也有一些注意事项…
-
性能问题:在章节中,我花费了额外的努力来最小化故事切换导致的速度下降。
-
测试和故事的耦合性:由于 Cypress 甚至都使用了 Storybook,故事不仅要为团队共享的设计系统负责,还要为组件测试负责。
-
回调测试变得困难:检查回调 props 的参数和调用变得困难。
我的实验中的一些问题可以通过 daedalius 的方法 缓解,但解决方案还不够理想,但是然后…
在 4 月 28 日,Cypress 4.5.0 版本发布了,唯一的功能更新如下
当将 experimentalComponentTesting 配置选项设置为 true 时,Cypress 现在支持使用特定于框架的适配器执行组件测试。有关详细信息,请参见 cypress-react-unit-test 和 cypress-vue-unit-test 仓库。
这是什么意思呢?这意味着 Cypress 现在可以直接挂载 React 组件,为 cypress-react-unit-test 带来了新的生机!在 Cypress 4.5.0 发布之前,该插件相当有限,但现在它得到了一流的支持!事实上,cypress-react-unit-test 现在非常可靠,是一个有意义的插件。
组件依然是 VirtualList,关于它的更多信息请查看上一章。我们需要同时设置 cypress-react-unit-test 和 TypeScript 转换(该组件使用 TypeScript 编写,是 Lerna monorepo 的一部分,并且使用 Webpack 进行编译)。这两个步骤都很简单,但是由于插件在其文档中有一个专门的安装部分,因此 TypeScript 编译可能不太明显,而且有许多过时或部分过时的不同方法和资源。
最简洁而有效的解决方案是André Pena 的方法,因此我只需要:
- 添加 cypress/webpack.config.js 文件
module.exports = {
mode: 'development',
devtool: false,
resolve: {
extensions: ['.ts', '.tsx', '.js'],
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: [/node_modules/],
use: [
{
loader: 'ts-loader',
options: {
// skip typechecking for speed
transpileOnly: true,
},
},
],
},
],
},
}
- 添加 cypress/tsconfig.json 文件
{
"extends": "../tsconfig.json",
"compilerOptions": {
"types": ["cypress", "cypress-wait-until"]
}
}
请注意:
-
../tsconfig.json 文件与 React 应用程序使用相同的文件
-
cypress-wait-until 不是必需的,但我经常使用它,它是 Cypress 最常安装的插件之一
上述与转译相关的文件,以及以下的 cypress.json 文件
{
"experimentalComponentTesting": true,
"componentFolder": "cypress/component"
}
以上就足够开始尝试编写 cypress/component/VirtualList.spec.tsx 测试了!在前一章中,第一个测试是标准渲染,即“当组件接收到 10000 个项时,只渲染最少数量的项”测试,就这样吧:
/// <reference types="Cypress" />
/// <reference types="cypress-wait-until" />
import React from 'react'
import { mount } from 'cypress-react-unit-test'
import '[@testing](http://twitter.com/testing)-library/cypress/add-commands'
import { VirtualList } from '../../src/atoms/VirtualList'
import { getStoryItems } from '../../stories/atoms/VirtualList/utils'
describe('VirtualList', () => {
it('When the list receives 10000 items, then only the minimum number of them are rendered', () => {
// Arrange
const itemsAmount = 10000
const itemHeight = 30
const listHeight = 300
const items = getStoryItems({ amount: itemsAmount })
const visibleItemsAmount = listHeight / itemHeight
// Act
mount(
<VirtualList
items={items}
getItemHeights={() => itemHeight}
RenderItem={createRenderItem({ height: itemHeight })}
listHeight={listHeight}
/>
)
// Assert
const visibleItems = items.slice(0, visibleItemsAmount - 1)
itemsShouldBeVisible(visibleItems)
// first not-rendered item check
cy.findByText(getItemText(items[visibleItemsAmount])).should('not.exist')
})
})
与 Storybook 相关的章节相比:
- 这
/// <reference types="Cypress" />
/// <reference types="cypress-wait-until" />
在开始时需要这些内容,以便让 VSCode 正确利用 TypeScript 的建议和错误报告
- 我们使用 cypress-react-unit-test 的 mount API 来挂载组件,如果你习惯于 Testing Library APIs,这并没有什么特别的新东西
没有更多,Cypress 测试与 Storybook 相关的测试一样 😊
将所有测试从上一章迁移过来相当容易,缺失的是“选择测试”的回调测试部分。
创建一个名为 WithSelectionManagement 的包装组件,它渲染 VirtualList 并管理项的选择是相当容易的,我们可以将我们的存根传递给它并对其进行断言。
it('When the items are clicked, then they are selected', () => {
const itemHeight = 30
const listHeight = 300
let testItems
const WithSelectionManagement: React.FC<{
testHandleSelect: (newSelectedIds: ItemId[]) => {}
}> = (props) => {
const { testHandleSelect } = props
const items = getStoryItems({ amount: 10000 })
const [selectedItems, setSelectedItems] = React.useState<(string | number)[]>([])
const handleSelect = React.useCallback<(params: OnSelectCallbackParams<StoryItem>) => void>(
({ newSelectedIds }) => {
setSelectedItems(newSelectedIds)
testHandleSelect(newSelectedIds)
},
[setSelectedItems, testHandleSelect]
)
React.useEffect(() => {
testItems = items
}, [items])
return (
<VirtualList
items={items}
getItemHeights={() => itemHeight}
listHeight={listHeight}
RenderItem={createSelectableRenderItem({ height: itemHeight })}
selectedItemIds={selectedItems}
onSelect={handleSelect}
/>
)
}
WithSelectionManagement.displayName = 'WithSelectionManagement'
mount(<WithSelectionManagement testHandleSelect={cy.stub().as('handleSelect')} />)
cy.then(() => expect(testItems).to.have.length.greaterThan(0))
cy.wrap(testItems).then(() => {
cy.findByText(getItemText(testItems[0])).click()
cy.get('[@handleSelect](http://twitter.com/handleSelect)').should((stub) => {
expect(stub).to.have.been.calledOnce
expect(stub).to.have.been.calledWith([testItems[0].id])
})
})
})
请参考完整的 SinonJS(由 Cypress 包装和使用)Stub/Spy 文档,了解详细的 API。
请查看此视频中的结果。现在测试只需要不到七秒,无需依赖或加载 Storybook,充分利用 Cypress 的一流支持。
接下来呢?cypress-react-unit-test 插件目前相当稳定和实用,一个全新的实验领域正向我们展开,许多中小型项目可以选择将 Cypress 作为唯一的测试工具。