unit-testing

GitHub

提供Lichtblick单体仓库的单元测试技能,基于ADR-0002规范。通过可复用的Mock Builders(如PlayerBuilder)自动生成有效测试数据,支持默认值、字段覆盖及复杂对象组合,提升测试代码的一致性与可读性。

.github/skills/unit-testing/SKILL.md lichtblick-suite/lichtblick

Trigger Scenarios

需要编写单元测试时 构建或修改测试数据时 使用Mock Builder模式时

Install

npx skills add lichtblick-suite/lichtblick --skill unit-testing -g -y
More Options

Non-standard path

npx skills add https://github.com/lichtblick-suite/lichtblick/tree/develop/.github/skills/unit-testing -g -y

Use without installing

npx skills use lichtblick-suite/lichtblick@unit-testing

指定 Agent (Claude Code)

npx skills add lichtblick-suite/lichtblick --skill unit-testing -a claude-code -g -y

安装 repo 全部 skill

npx skills add lichtblick-suite/lichtblick --all -g -y

预览 repo 内 skill

npx skills add lichtblick-suite/lichtblick --list

SKILL.md

Frontmatter
{
    "name": "unit-testing",
    "description": "Unit testing patterns, mock builder usage, and test data construction strategies for the Lichtblick monorepo."
}

Unit Testing Skill

Mock Builders (ADR-0002)

The project follows ADR-0002: Reusable Mock Builders for Testing — a Builder pattern that centralizes test data creation across unit, integration, and E2E tests.

Why Builders?

  • Reusability: Same builders used in unit, integration, and E2E tests
  • Consistency: All tests get valid, well-formed test data by default
  • Flexibility: Override only the fields relevant to the test, rest auto-populated
  • Readability: Tests focus on behavior, not data setup boilerplate

Architecture

@lichtblick/test-builders          (external published npm dependency, not a workspace package)
├── BasicBuilder                    (primitives: string, number, boolean, date, lists, etc.)
└── defaults<T>(overrides, defaults) (utility to merge partial overrides with defaults)

@lichtblick/suite-base/testing/builders/  (domain builders)
├── PlayerBuilder.ts                (PlayerState, Topic, TopicStats, ActiveData)
├── MessageEventBuilder.ts          (MessageEvent with topic + data)
├── RosTimeBuilder.ts               (Time { sec, nsec })
├── RosDatatypesBuilder.ts          (MessageDefinition, datatypes maps)
├── LayoutBuilder.ts                (Layout configs, global variables)
├── RenderStateBuilder.ts           (Panel renderState)
├── ExtensionBuilder.ts             (Extension metadata)
├── PlotBuilder.ts                  (Plot panel data)
├── DiagnosticsBuilder.ts           (DiagnosticStatusArray)
├── SettingsTreeNodeBuilder.ts      (Settings tree nodes)
├── InitializationSourceBuilder.ts   (Data source initialization)
└── ... (more domain-specific builders)

Usage Pattern

import { BasicBuilder } from "@lichtblick/test-builders";
import PlayerBuilder from "@lichtblick/suite-base/testing/builders/PlayerBuilder";

// Create with all defaults — valid data without specifying anything
const topic = PlayerBuilder.topic();

// Override only what matters for this test
const customTopic = PlayerBuilder.topic({ name: "/camera/image" });

// Generate multiple instances
const topics = PlayerBuilder.topics(5);

// Compose builders for complex objects
const state = PlayerBuilder.playerState({
  activeData: PlayerBuilder.activeData({
    topics: [PlayerBuilder.topic({ name: "/lidar" })],
    currentTime: RosTimeBuilder.time({ sec: 100, nsec: 0 }),
  }),
});

Creating a New Builder

Follow the existing pattern when adding builders for new domain types:

import { BasicBuilder, defaults } from "@lichtblick/test-builders";

class MyEntityBuilder {
  // Static factory method with partial overrides
  public static myEntity(props: Partial<MyEntity> = {}): MyEntity {
    return defaults<MyEntity>(props, {
      id: BasicBuilder.string(),
      name: BasicBuilder.string(),
      count: BasicBuilder.number(),
      enabled: BasicBuilder.boolean(),
      createdAt: BasicBuilder.date(),
    });
  }

  // Plural helper for generating lists
  public static myEntities(count = 3): MyEntity[] {
    return BasicBuilder.multiple(MyEntityBuilder.myEntity, count);
  }
}

export default MyEntityBuilder;

Key Principles

  1. Default values must produce valid objects — a builder with no overrides should return a structurally correct instance
  2. Use defaults<T>() helper — merges partial overrides with generated defaults
  3. Static methods only — builders are stateless utility classes, no instances needed
  4. Compose builders — entity builders call other builders for nested types (e.g., PlayerBuilder calls RosTimeBuilder)
  5. Place builders in correct scope:
    • Shared primitives → @lichtblick/test-builders (BasicBuilder)
    • Domain entities → @lichtblick/suite-base/testing/builders/
    • Component-specific test data → colocated builders/ directory within the component folder

BasicBuilder API

The BasicBuilder class (from @lichtblick/test-builders) provides:

Method Returns
BasicBuilder.string() Random string
BasicBuilder.number() Random number
BasicBuilder.boolean() Random boolean
BasicBuilder.date() Random Date
BasicBuilder.strings(count?) Array of strings
BasicBuilder.sample(array, count?) Random sample from array
BasicBuilder.multiple(factory, count?) Array of N items from factory function
BasicBuilder.genericMap(valueFn) Map with generated keys/values
BasicBuilder.genericDictionary(valueFn) Record<string, T> with generated entries

Anti-patterns

  • Don't inline test data when a builder exists — use PlayerBuilder.topic() instead of { name: "/foo", schemaName: "bar" }
  • Don't copy-paste builder data between tests — extract to a builder method
  • Don't assert on random builder values — override the specific field you're testing, then assert on that known value
  • Don't use builders to test the builder output — test actual behavior

Test Structure Reference

See the Unit Test Agent (.github/agents/lb-unit-test.agent.md) for:

  • Given-When-Then (GWT) pattern
  • Naming conventions
  • Mocking patterns (modules, workers, timers)
  • Test quality rules

Version History

  • cab9317 Current 2026-07-24 12:17

Same Skill Collection

.github/skills/3d-rendering/SKILL.md
.github/skills/caching-internals/SKILL.md
.github/skills/deserialization/SKILL.md
.github/skills/e2e-playwright-mcp/SKILL.md
.github/skills/electron-internals/SKILL.md
.github/skills/extensions-internals/SKILL.md
.github/skills/layouts-internals/SKILL.md
.github/skills/mcap-format/SKILL.md
.github/skills/message-path/SKILL.md
.github/skills/message-pipeline/SKILL.md
.github/skills/panel-extension-api/SKILL.md
.github/skills/panel-image/SKILL.md
.github/skills/panel-log/SKILL.md
.github/skills/panel-map/SKILL.md
.github/skills/panel-raw-messages/SKILL.md
.github/skills/panel-state-transitions/SKILL.md
.github/skills/panel-user-scripts/SKILL.md
.github/skills/performance/SKILL.md
.github/skills/player-internals/SKILL.md
.github/skills/plot-internals/SKILL.md
.github/skills/remote-caching/SKILL.md
.github/skills/test-conventions/SKILL.md
.github/skills/theme/SKILL.md
.github/skills/web-workers/SKILL.md
.github/skills/websocket-connection/SKILL.md

Metadata

Files
0
Version
cab9317
Hash
880733d9
Indexed
2026-07-24 12:17

trang chủ - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-08 03:41
浙ICP备14020137号-1 $bản đồ khách truy cập$