unit-tests

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Unit 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 testingWhy
Pure functions / modulesStable inputs → outputs; fast; no DOM
Formatters, parsers, scale math, boundsRegression on string/number output is user-visible
Codegen / export / registry helpersOutput shape is the contract
Non-obvious edge casesEmpty data, reversed ranges, clamping
Examples in this repo:
chart-formatters.test.ts
,
highlight-segment-bounds.test.ts
,
animation.test.ts
,
apps/web/lib/studio/__tests__/*
.

当测试能够锁定易被静默破坏的行为时,才添加测试:
值得测试的内容原因
纯函数/模块输入输出稳定;执行快速;无需操作DOM
格式化器、解析器、比例计算、边界逻辑字符串/数字输出的回归会影响用户体验
代码生成/导出/注册表辅助工具输出结构是对外的契约
非直观的边缘场景空数据、反转范围、值限制等
本仓库示例:
chart-formatters.test.ts
highlight-segment-bounds.test.ts
animation.test.ts
apps/web/lib/studio/__tests__/*

When NOT to add tests (unless explicitly requested)

何时不添加测试(除非明确要求)

Do not add tests just to increase coverage or “be thorough”:
SkipWhy
React component render smoke testsvisx, motion, portals → brittle; manual/docs check is cheaper
memo()
/ guard extractions (#65-style)
Structural perf refactors; output unchanged; RTL mount assertions are heavy
Default prop passthrough
formatValue = intFmt
— types + formatter tests cover it
Third-party library behaviorDon’t re-test d3, visx, or React
Trivial getters / one-line wrappersNo regression signal
Snapshot entire chart SVG/JSXHigh 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等会导致测试脆弱;手动检查或文档验证成本更低
memo()
/防护提取(#65风格)
结构性性能重构不会改变输出;RTL挂载断言开销大
默认属性传递
formatValue = intFmt
—— 类型检查+格式化器测试已覆盖该逻辑
第三方库行为无需重复测试d3、visx或React
简单的getter/单行包装器不会提供回归预警信号
快照整个图表SVG/JSX变更频率高,价值低
如果用户询问“我们是否应该测试X?”——当X属于上表中的内容时,建议不测试,并提供更轻量的替代方案(纯辅助函数测试、手动检查、CI构建验证)。

Repo conventions

仓库约定

Runner: Node built-in test runner +
tsx
(not Jest/Vitest unless the repo adopts them later).
bash
pnpm test              # root — turbo runs packages with a test script
pnpm --filter @bklitui/ui test
cd apps/web && pnpm test
Place tests:
**/__tests__/**/*.test.ts
next to the code under test.
Pattern:
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.
toLocaleDateString
with same locale/options) so tests stay timezone-safe and prove no visual regression.
New package test script: add to
package.json
:
json
"test": "node scripts/run-tests.mjs"
Use a small
scripts/run-tests.mjs
that collects
*.test.ts
from
__tests__
and invokes
node --import tsx --test
— shell globs break on Linux CI. Add
tsx
as a devDependency if missing. Wire into root
turbo.json
test
task; CI runs
pnpm test
.

运行器: Node内置测试运行器 +
tsx
(除非仓库后续采用Jest/Vitest,否则不使用)。
bash
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.json
中添加:
json
"test": "node scripts/run-tests.mjs"
使用一个小型的
scripts/run-tests.mjs
脚本,从
__tests__
中收集
*.test.ts
文件并调用
node --import tsx --test
——Shell通配符在Linux CI上会失效。如果缺少
tsx
,将其添加为开发依赖。在根目录的
turbo.json
test
任务中配置;CI会运行
pnpm test

Decision checklist (run before writing)

决策检查清单(编写前执行)

  1. Is the logic pure or extractable to pure functions? → Test that. Consider extracting first.
  2. Would a test fail on a real user-visible bug? → Good candidate.
  3. Does it need jsdom / RTL / Playwright? → Stop; justify to user or defer to manual/visual check.
  4. Did the user ask for tests? → Still apply this skill; don’t over-deliver.
  5. How many cases? → 3–10 focused cases, not exhaustive matrices.

  1. 逻辑是否为纯函数或可提取为纯函数?→ 测试该纯函数。考虑先提取逻辑。
  2. 测试是否会在真实用户可见的bug出现时失败?→ 是合适的测试候选。
  3. 是否需要jsdom / RTL / Playwright?→ 停止;向用户说明理由,或改用手动/视觉检查。
  4. 用户是否明确要求测试?→ 仍需遵循本技能指南;不要过度交付。
  5. 需要多少测试用例?→ 3–10个聚焦的用例,而非详尽的矩阵。

What to tell the user

如何回复用户

When recommending tests, be explicit:
  • Add: “Test
    chart-formatters.ts
    — pure, high regression value.”
  • Skip: “Skip component tests for the memo split — no output change; build + manual chart docs are enough.”
  • CI: Mention
    pnpm test
    in PR test plan when adding or changing tests.

推荐测试时,请明确说明:
  • 建议添加: “测试
    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
    useMemo
    call counts
  • 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')
  • 提交因硬编码时区/区域设置字符串而仅能在本地通过的测试