结合 Storybook、Cypress 和 Jest 代码覆盖率
代码覆盖率
测试类型
这篇文章介绍了如何结合使用Storybook、Cypress和Jest代码覆盖率,并解释了为什么您可能需要这样做。完整代码可在 GitHub 上获取。
代码覆盖率
了解代码库中有多少部分以及哪些部分已被某种形式的测试覆盖,有助于指导未来的测试工作。使用Codecov等工具可以告知审阅者拉取请求是否会增加或减少整体测试覆盖率——提醒审阅者在添加新功能或修复错误时检查是否已编写适当的测试。
但是,您可能在不同的系统中运行不同类型的测试,因此仅来自一种测试类型的代码覆盖率指标可能无法提供足够的信息。
测试类型
选择正确的测试类型取决于您正在测试的代码类型:
由路由器组成并连接到 API 的代码最好通过针对模拟或预先记录的 API 的集成测试进行测试,例如使用Cypress、Selenium。
实用程序代码(例如字符串格式化程序、正则表达式和一些 React 钩子或组件)最好通过单元测试来处理,例如使用Jest、React 测试库。
哑组件和样式(例如主要用于提供语义标记或 CSS、CSS 文件、CSSinJS 的 React 组件)可能最好使用视觉回归测试来覆盖,例如使用Storybook结合Chromatic。
布局
示例应用程序的布局如下:
- 应用
**/*.spec.js
(集成测试)**/styles/*.stories.js
(视觉回归测试)**/styles/*.js
(不属于设计系统的样式)**/*.test.js
(单元测试)**/*.js
(申请代码)
- 公用事业
**/*.test.js
(单元测试)**/*.js
(实用代码)
- 设计系统
**/*.stories.js
(视觉回归测试)**/*.js
(设计系统代码)
在这种模式下,您可能需要设置一些目标代码覆盖率指标,例如:
utilities
单元测试覆盖率 100%design-system
视觉回归测试覆盖率 100%application
拆分为:**/styles/*.js
视觉回归测试覆盖率至少为 XX%- 所有其他代码至少被单元测试或集成测试覆盖 XX%
- 所有代码均通过任何类型的测试覆盖率 >90%
但是我们如何获得这些指标呢?我们如何获得总体覆盖率值呢?
示例设置
我创建了一个示例项目,其中显示以下内容:
- Cypress 的集成测试代码覆盖率指标
- Jest 的单元测试代码覆盖率指标
- Storybook 中的代码覆盖率指标,用于视觉回归测试
- 结合以上 3 个覆盖率报告来显示整体代码覆盖率
https://github.com/penx/storybook-code-coverage
集成测试
在 create-react-app 应用程序中获取 Cypress 测试的代码覆盖率需要以下库:
- @cypress/instrument-cra用于检测应用程序(允许收集覆盖范围)
- @cypress/code-coverage报告结果
为了通过一些基本配置和测试来搭建一个 Cypress 项目,我使用了@bahmutov/cly,并参考了以下博客文章:
- https://www.cypress.io/blog/2019/05/13/code-create-react-app-v3-and-its-cypress-tests-using-typescript/
- https://www.cypress.io/blog/2019/09/05/cypress-code-coverage-for-create-react-app-v3/
并按照@cypress/code-coverage 设置说明执行以下操作:
添加到您的 cypress/support/index.js 文件
import '@cypress/code-coverage/support'
在 cypress/plugins/index.js 文件中注册任务
require('@cypress/code-coverage/task')(on, config)
为了在运行 Cypress 测试时自动启动应用程序,我使用了以下库:
@cypress/instrument-cra 不会收集未被 webpack 加载的文件的元数据。为了解决这个问题,我通过在.nyc_output/out.json
运行 Cypress 测试之前运行一个模拟测试来创建一个初始文件。
我在项目根目录中名为“fake.test.js”的文件中添加了虚假测试:
it("shall pass", () => {});
下面的(略微复杂的)“coverage:init”测试使用了此测试。它允许我们运行 create-react-app 的代码覆盖率脚本,但覆盖率为零,从而生成一个包含代码覆盖率元数据的 json 文件,但实际上并没有覆盖率。说实话,可能有更简洁的方法来做到这一点。
以下 nyc 设置已添加到 package.json:
"nyc": {
"report-dir": "coverage/integration",
"reporter": ["text", "json", "lcov"],
"all": true,
"include": [
"src/**/*.js"
],
"exclude": [
"**/*.test.js",
"**/test.js",
"**/*.stories.js",
"**/stories.js"
]
},
连同以下脚本(请注意默认启动脚本的更改):
"start": "react-scripts -r @cypress/instrument-cra start",
"coverage:init": "react-scripts test --watchAll=false --coverage --coverageDirectory=.nyc_output --roots=\"<rootDir>\" --testMatch=\"<rootDir>/fake.test.js\" --coverageReporters=json && mv .nyc_output/coverage-final.json .nyc_output/out.json",
"test:integration": "cypress run",
"coverage:integration": "start-server-and-test 3000 test:integration",
其结果如下:
然后,我可以通过打开 lcov 报告来更详细地了解这些指标coverage/integration/lcov-report/index.html
。
浏览报告中的 src/application/App.js 我可以看到未覆盖的分支(黄色)和线条(红色):
视觉回归测试
为了从 Storybook 中提取代码覆盖率,我曾经@storybook/addon-storyshots
生成过 Jest 快照。这些快照每次都会创建,并且不会与现有快照进行比较。它们不用于跟踪更改,只是作为 Jest 的一个钩子来收集覆盖率。
(2022 年 11 月更新:查看@storybook/test-runner和@storybook/addon-coverage作为 Storyshots 的替代品)
Storyshots 的设置与文档中所述一致,并添加了“renderOnly”,这样我们就不会将快照保存到磁盘。
在./storyshots/index.js
:
import initStoryshots, {renderOnly} from '@storybook/addon-storyshots';
initStoryshots({test: renderOnly});
然后在package.json中添加以下脚本:
"coverage:visual-regression": "react-scripts test --watchAll=false --coverage --coverageDirectory=coverage/visual-regression --roots=\"<rootDir>\" --testMatch=\"<rootDir>/storyshots/index.js\"",
当你运行这个脚本时你应该看到如下内容:
再次,我可以查看 lcov 报告(coverage/visual-regression/lcov-report/index.html)以了解更多详细信息:
单元测试
这相当简单,因为它主要使用 create-react-app 提供的功能 - 尽管create-react-app@3.4.1 中存在一个错误,导致它无法工作,所以现在最好坚持使用 3.4.0。
需要进行一些小的调整:
- 通过将其添加到 package.json 中,告诉 create react app 不要收集故事的报道:
"jest": {
"collectCoverageFrom": [
"src/**/*.js",
"!**/*.test.js",
"!**/test.js",
"!**/*.stories.js",
"!**/stories.js"
]
},
- 使用 react-scripts 创建一个从所有文件收集覆盖率的脚本:
{
"coverage:unit": "react-scripts test --watchAll=false --coverage --coverageDirectory=coverage/unit",
}
当你运行这个脚本时你应该看到如下内容:
已合并
我建议使用Codecov,它可以为您合并报告并将指标作为拉取请求的评论发布- 但是在这个例子中,我正在寻找可以在本地运行以生成合并报告的东西。
我使用 istanbul-merge 生成合并报告,并在 package.json 中使用以下脚本:
"coverage": "yarn coverage:clean && yarn coverage:init && yarn coverage:integration && yarn coverage:unit && yarn coverage:visual-regression && yarn coverage:merge && yarn coverage:merge-report",
"coverage:clean": "rm -rf .nyc_output && rm -rf coverage",
"coverage:merge": "istanbul-merge --out coverage/merged/coverage-final.json ./coverage/unit/coverage-final.json ./coverage/visual-regression/coverage-final.json ./coverage/integration/coverage-final.json",
"coverage:merge-report": "nyc report --reporter=lcov --reporter=text --temp-dir=./coverage/merged --report-dir=./coverage/merged"
运行时yarn coverage
我现在得到了以上所有内容以及以下合并报告:
现在我有了这份报告,我就可以寻找需要关注的领域了。
例如,我觉得很奇怪,即使运行了所有单元、视觉回归和单元测试之后,我仍然没有 100% 覆盖 GlobalStyles。
我可以深入研究 lcov 报告来找出原因:
我没有测试过黑暗模式!😢
文章来源:https://dev.to/penx/combining-storybook-cypress-and-jest-code-coverage-4pa5