开源数据发现平台:Amundsen Frontend Service 推荐实践
Amundsen 是一个数据发现和元数据引擎,旨在提高数据分析师、数据科学家和工程师与数据交互时的生产力。目前,它通过索引数据资源(表格、仪表板、数据流等)并基于使用模式(例如,查询频率高的表格会优先于查询频率低的表格)提供页面排名式的搜索功能来实现这一目标。您可以将其视为数据版的 Google 搜索。该项目以挪威探险家罗尔德·阿蒙森 (Roald Amundsen) 的名字命名,他是第一个发现南极的人。
推荐实践
本文档作为当前实践与模式的参考,旨在标准化 Amundsen 前端应用代码。我们在下方提供了面向新贡献者或尚不具备特定框架或核心库领域知识的贡献者的高级指南。本文档并非意在提供完成某些任务的详尽清单。
我们希望通过这些实践维持一个相当一致的代码库,并欢迎通过 PR 更新和改进这些建议。
代码风格指南
我们在 ESLint 与 Prettier 规则 中内置了 TypeScript 代码的编码风格指南,并在 Stylelint 规则 中内置了 Sass 代码的编码风格指南。
展望未来,我们计划使用 ESLint 和 Stylelint 设定更严格的最佳实践。为此,我们利用名为 betterer 的项目,当给定测试通过时,它会跟踪我们的错误。你可以通过运行 npm run betterer
来执行它,如果你引入任何新的 eslint 错误,它将中断。如果你想忽略新错误,可以运行 npm run betterer:update
来更新 betterer.results 文件。我们不建议添加或引入新的 eslint 错误。
命名规范
React 对我们的组件命名并不固执己见。拥有自由很好,但有时这会以 API 缺乏一致性为代价。
为确保 Amundsen 的 React 应用的一致性和直观性,我们创建了这份文档,描述 React 组件的命名规范。
组件名称
视图组件
我们将遵循以下规范命名常规“视图”组件:
[上下文]组件名称[类型]
其中:
- 上下文 - 父组件或高级页面
- 组件名称 - 此组件的作用。组件的职责
- 类型 - 组件的类型。它们通常是视图,但也可能是表单、列表、图形、插图、容器
示例:
- SideBar(根组件)
- FooterSideBar(带上下文)
- SideBarForm(带组件类型)
- FooterSideBarForm(带所有)
自定义 Hook 组件
我们将以“use”开头命名自定义 hook 组件,如官方文档所述。
高阶组件
我们将使用“with”前缀命名高阶组件(HOC)。例如:
- withAuthentication
- withSubscription
Provider/Consumer 组件
每当我们需要使用 Provider/Consumer 模式并命名组件时,我们将使用“Provider”和“Consumer”后缀。例如:
- LoginProvider/LoginConsumer
- DataProvider/DataConsumer
属性名称
处理函数
我们将使用“on”前缀作为处理函数,可选地在事件前添加“主题”一词。模式为“on[主题]事件”,例如:
- onClick
- onItemClick
- onLogoHover
- onFormSubmit
在内部,我们将使用“handle”前缀命名处理函数,中间可选加“主题”一词。模式为“handle[主题]事件”,例如:
- handleClick
- handleButtonClick
- handleHeadingHover
按类型划分的属性
属性应命名以描述组件本身及其作用,但不描述它为何这样做。
- 对象 - 使用单数名词
- item
- 数组 - 使用复数名词
- items
- users
- 数字 - 使用“num”前缀或“count”或“index”后缀的名称。
- numItems
- userCount
- itemIndex
- 布尔值 - 使用“is”、“has”或“can”前缀
- “is”用于视觉变化
- isVisible
- isEnabled
- isActive
- “is”也用于行为或条件变化
- isToggleable
- isExpandable
- “has”用于切换 UI 元素
- hasCancelSection
- hasHeader
- “is”用于视觉变化
- React 节点 - 使用“element”后缀
- buttonElement
- itemElement
单元测试
我们使用 Jest 作为测试框架。我们利用 Enzyme 的实用方法测试 React 组件,并使用 redux-saga-test-plan 测试我们的 redux-saga
中间件逻辑。
通用
- 利用 TypeScript 防止单元测试中的错误,并确保使用与定义接口和类型匹配的输入测试代码。添加和更新测试 fixtures 有助于为此目的提供可重用的类型化测试数据或模拟实现。
- 适用时利用
beforeAll()
/beforeEach()
进行测试设置。适用时利用afterAll()
/afterEach
进行测试拆卸,以移除测试块的任何副作用。例如,如果在beforeAll()
中创建了方法的模拟实现,应在afterAll()
中恢复原始实现。参见 Jest 的 setup-teardown 文档 以获得进一步理解。 - 使用描述性标题字符串。为了协助调试,我们应能理解测试检查的内容以及在什么条件下。
- 熟悉各种可用的 Jest 匹配器。理解不同匹配器的细微差别及各自理想情况有助于编写更健壮的测试。例如,验证对象有许多不同方式,最佳匹配器取决于我们具体测试的内容。示例:
- 如果断言
inputObject
赋值给变量x
,使用.toBe()
断言x
的等价性会为此案例创建更健壮的测试,因为.toBe()
将验证变量是否实际引用给定对象。相比之下,像.toEqual()
这样的匹配器将验证对象是否恰好具有特定属性和值。在此情况下使用.toEqual()
会隐藏错误的风险,即x
并未如预期实际引用inputObject
,但由于代码中的副作用,恰好具有相同的键值对。 - 如果断言
outputObject
匹配expectedObject
,使用.toBe()
断言outputObject
每个属性的等价性或使用.toMatchObject()
断言两个对象的等价性,在我们只关心outputObject
上存在某些值时很有用。然而,如果某些值不存在于outputObject
上很重要——如 reducer 输出的情况——.toEqual()
是更健壮的替代方案,因为它比较两个对象上所有属性的等价性。
- 如果断言
- 当测试使用 JavaScript 的 Date 对象的逻辑时,请注意我们的 Jest 脚本配置为在 UTC 时区运行。开发人员应:
- 模拟 Date 对象及其方法的返回值,并基于模拟值运行断言。
- 创建断言时知道单元测试套件将按 UTC 时区运行。
- 代码覆盖率很重要,但它仅告知我们在测试期间实际运行和执行了哪些代码。开发人员有责任关注用例覆盖率,并确保运行正确的断言,以便充分测试所有逻辑。
React
- Enzyme 为测试提供了 3 种不同的渲染 React 组件的工具。我们建议使用
mount
渲染,以便你可以深入渲染输出。 - 创建一个可重用的
setup()
函数,它将接受测试条件逻辑所需的任何参数。 - 寻找以某种方式组织测试的机会,使得一个
setup()
可用于测试在相同条件下发生的断言。例如,对于没有条件逻辑的方法,测试块应只有一个setup()
。然而,不建议跨不同方法的测试或跨依赖于可变状态片段的方法的测试共享setup()
结果。原因是我们冒着将副作用从一个测试块传播到另一个测试块的风险。 - 如果组件或其他文件变得难以测试,请考虑重构。可能的选项包括(但不限于):
- 为大型组件创建子组件。这在组件布局更改时也特别有助于减少更新测试的负担。
- 将大型函数分解为更小的函数。分别对较小函数的逻辑进行单元测试,并在测试较大函数时模拟其结果。
- 将常量从单独文件导出,用于硬编码值,并将其导入相关源文件和测试文件。这对字符串尤其有帮助。
Redux
- 由于大多数 Redux 代码由函数组成,我们像往常一样对这些方法进行单元测试,并确保函数为任何给定输入产生预期输出。参见 Redux 关于测试 action creators、async action creators 和 reducers 的文档,或查看我们代码中的示例。
- 除非 action creator 包含除返回 action 之外的任何逻辑,否则对 reducer 和 saga 中间件逻辑进行单元测试已足够,且最具价值。
redux-saga
生成器函数可以通过逐步迭代并运行每一步的断言来测试,或通过执行整个 saga 并对副作用运行断言。参见 redux-saga 关于测试 sagas 的文档,以获取更广泛的示例。
参考
- 如何为 React 组件命名属性 – David 的博客
- React 事件处理函数和属性命名约定技巧 | 作者:Juan Bernal | JavaScript In Plain English | Medium
- https://medium.com/@wittydeveloper/react-components-naming-convention-%EF%B8%8F-b50303551505
- https://reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook
- https://reactjs.org/docs/higher-order-components.html
风险提示与免责声明
本文内容基于公开信息研究整理,不构成任何形式的投资建议。历史表现不应作为未来收益保证,市场存在不可预见的波动风险。投资者需结合自身财务状况及风险承受能力独立决策,并自行承担交易结果。作者及发布方不对任何依据本文操作导致的损失承担法律责任。市场有风险,投资须谨慎。