unit-tests
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseUnit Tests (bklit-ui)
Unit Tests (bklit-ui)
Read this skill before proposing or writing tests. Default stance: fewer, higher-signal tests.
在提议或编写测试前,请阅读本技能指南。默认原则:更少但更具价值的测试。
When to add tests
何时添加测试
Add tests when they lock in behavior that is easy to break silently:
| Worth testing | Why |
|---|---|
| Pure functions / modules | Stable inputs → outputs; fast; no DOM |
| Formatters, parsers, scale math, bounds | Regression on string/number output is user-visible |
| Codegen / export / registry helpers | Output shape is the contract |
| Non-obvious edge cases | Empty data, reversed ranges, clamping |
Examples in this repo: , , , .
chart-formatters.test.tshighlight-segment-bounds.test.tsanimation.test.tsapps/web/lib/studio/__tests__/*当测试能够锁定易被静默破坏的行为时,才添加测试:
| 值得测试的内容 | 原因 |
|---|---|
| 纯函数/模块 | 输入输出稳定;执行快速;无需操作DOM |
| 格式化器、解析器、比例计算、边界逻辑 | 字符串/数字输出的回归会影响用户体验 |
| 代码生成/导出/注册表辅助工具 | 输出结构是对外的契约 |
| 非直观的边缘场景 | 空数据、反转范围、值限制等 |
本仓库示例: 、、、。
chart-formatters.test.tshighlight-segment-bounds.test.tsanimation.test.tsapps/web/lib/studio/__tests__/*When NOT to add tests (unless explicitly requested)
何时不添加测试(除非明确要求)
Do not add tests just to increase coverage or “be thorough”:
| Skip | Why |
|---|---|
| React component render smoke tests | visx, motion, portals → brittle; manual/docs check is cheaper |
| Structural perf refactors; output unchanged; RTL mount assertions are heavy |
| Default prop passthrough | |
| Third-party library behavior | Don’t re-test d3, visx, or React |
| Trivial getters / one-line wrappers | No regression signal |
| Snapshot entire chart SVG/JSX | High churn, low signal |
If the user asks “should we test X?” — say no when X falls in this table, and suggest a lighter alternative (pure helper test, manual check, CI build).
不要仅仅为了提高覆盖率或“追求全面”而添加测试:
| 跳过测试的内容 | 原因 |
|---|---|
| React组件渲染冒烟测试 | visx、motion、portals等会导致测试脆弱;手动检查或文档验证成本更低 |
| 结构性性能重构不会改变输出;RTL挂载断言开销大 |
| 默认属性传递 | |
| 第三方库行为 | 无需重复测试d3、visx或React |
| 简单的getter/单行包装器 | 不会提供回归预警信号 |
| 快照整个图表SVG/JSX | 变更频率高,价值低 |
如果用户询问“我们是否应该测试X?”——当X属于上表中的内容时,建议不测试,并提供更轻量的替代方案(纯辅助函数测试、手动检查、CI构建验证)。
Repo conventions
仓库约定
Runner: Node built-in test runner + (not Jest/Vitest unless the repo adopts them later).
tsxbash
pnpm test # root — turbo runs packages with a test script
pnpm --filter @bklitui/ui test
cd apps/web && pnpm testPlace tests: next to the code under test.
**/__tests__/**/*.test.tsPattern:
ts
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { myFn } from "../my-module";
describe("myFn", () => {
it("handles empty input", () => {
assert.equal(myFn([]), expected);
});
});Equivalence tests (preferred for formatters): assert shared module output matches the previous inline call (e.g. with same locale/options) so tests stay timezone-safe and prove no visual regression.
toLocaleDateStringNew package test script: add to :
package.jsonjson
"test": "node scripts/run-tests.mjs"Use a small that collects from and invokes — shell globs break on Linux CI. Add as a devDependency if missing. Wire into root task; CI runs .
scripts/run-tests.mjs*.test.ts__tests__node --import tsx --testtsxturbo.jsontestpnpm test运行器: Node内置测试运行器 + (除非仓库后续采用Jest/Vitest,否则不使用)。
tsxbash
pnpm test # 根目录 —— turbo会运行带有test脚本的包
pnpm --filter @bklitui/ui test
cd apps/web && pnpm test测试文件位置: 将测试文件放在被测试代码旁的路径下。
**/__tests__/**/*.test.ts代码模板:
ts
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { myFn } from "../my-module";
describe("myFn", () => {
it("handles empty input", () => {
assert.equal(myFn([]), expected);
});
});等价测试(格式化器优先采用):断言共享模块的输出与之前的内联调用(例如使用相同区域设置/选项的)一致,确保测试不受时区影响且能验证无视觉回归。
toLocaleDateString新增包的测试脚本: 在中添加:
package.jsonjson
"test": "node scripts/run-tests.mjs"使用一个小型的脚本,从中收集文件并调用——Shell通配符在Linux CI上会失效。如果缺少,将其添加为开发依赖。在根目录的的任务中配置;CI会运行。
scripts/run-tests.mjs__tests__*.test.tsnode --import tsx --testtsxturbo.jsontestpnpm testDecision checklist (run before writing)
决策检查清单(编写前执行)
- Is the logic pure or extractable to pure functions? → Test that. Consider extracting first.
- Would a test fail on a real user-visible bug? → Good candidate.
- Does it need jsdom / RTL / Playwright? → Stop; justify to user or defer to manual/visual check.
- Did the user ask for tests? → Still apply this skill; don’t over-deliver.
- How many cases? → 3–10 focused cases, not exhaustive matrices.
- 逻辑是否为纯函数或可提取为纯函数?→ 测试该纯函数。考虑先提取逻辑。
- 测试是否会在真实用户可见的bug出现时失败?→ 是合适的测试候选。
- 是否需要jsdom / RTL / Playwright?→ 停止;向用户说明理由,或改用手动/视觉检查。
- 用户是否明确要求测试?→ 仍需遵循本技能指南;不要过度交付。
- 需要多少测试用例?→ 3–10个聚焦的用例,而非详尽的矩阵。
What to tell the user
如何回复用户
When recommending tests, be explicit:
- Add: “Test — pure, high regression value.”
chart-formatters.ts - Skip: “Skip component tests for the memo split — no output change; build + manual chart docs are enough.”
- CI: Mention in PR test plan when adding or changing tests.
pnpm test
推荐测试时,请明确说明:
- 建议添加: “测试——纯函数,回归价值高。”
chart-formatters.ts - 建议跳过: “跳过memo拆分的组件测试——输出无变化;构建+手动检查图表文档即可。”
- CI相关: 添加或修改测试时,在PR测试计划中提及。
pnpm test
Anti-patterns
反模式
- Adding Jest/Vitest/Testing Library for a one-off without team buy-in
- Mocking entire chart context to assert call counts
useMemo - Testing implementation details (hook order, internal component names)
- Duplicating type-checker work ()
expect(typeof x).toBe('function') - Committing tests that only pass locally due to hard-coded timezone/locale strings
- 在未获得团队认可的情况下,为单次测试引入Jest/Vitest/Testing Library
- 模拟整个图表上下文以断言的调用次数
useMemo - 测试实现细节(钩子顺序、内部组件名称)
- 重复类型检查器的工作(如)
expect(typeof x).toBe('function') - 提交因硬编码时区/区域设置字符串而仅能在本地通过的测试