vitest-bdd-3layer

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

目的

Purpose

在 DashPlayer 中用 Vitest + 三层模型 编写 BDD 测试,让“场景编写”与“实现细节”解耦。
目标效果:
  • 场景层只写业务句子(给定/当/那么)
  • 步骤层维护业务动作词典
  • 实现层收敛 mock / fixture / adapter
Write BDD tests in DashPlayer using Vitest + Three-layer Model to decouple "scenario writing" from "implementation details".
Target Outcomes:
  • Scenario layer only contains business sentences (Given/When/Then)
  • Step layer maintains a dictionary of business actions
  • Implementation layer centralizes mocks / fixtures / adapters

适用范围

Scope of Application

  • 业务规则清晰、需要可读验收场景的模块
  • service + IPC + renderer 契约验证
  • 希望后续可扩展为更多业务域(settings / ai-trans / watch-history)
不适用:
  • 纯算法工具函数(普通单测更高效)
  • 高波动、易 flaky 的强时序用例
  • Modules with clear business rules that require readable acceptance scenarios
  • Contract verification for service + IPC + renderer
  • Modules expected to be extended to more business domains later (settings / ai-trans / watch-history)
Not Applicable:
  • Pure algorithm utility functions (regular unit tests are more efficient)
  • Highly volatile, easily flaky strong timing use cases

三层目录规范(必须遵守)

Mandatory Three-layer Directory Structure

settings
为例:
  1. 场景层(Scenario Layer)
    • src/backend/application/services/__tests__/Xxx.bdd.test.ts
    • 只能写
      given/when/then
      业务语句
    • 禁止直接
      new Service
      vi.spyOn
      store.set/get
  2. 步骤词典层(Step Dictionary Layer)
    • src/backend/application/services/__tests__/bdd/xxx.steps.ts
    • 用英文方法名组织能力:
      given* / when* / then*
    • 场景层仅调用步骤层方法
  3. 实现层(Fixture/Adapter Layer)
    • src/backend/application/services/__tests__/bdd/xxx.fixture.ts
    • 统一管理 mock、fixture、依赖注入与底层调用
Take
settings
as an example:
  1. Scenario Layer
    • src/backend/application/services/__tests__/Xxx.bdd.test.ts
    • Only write
      given/when/then
      business statements
    • Direct use of
      new Service
      ,
      vi.spyOn
      ,
      store.set/get
      is prohibited
  2. Step Dictionary Layer
    • src/backend/application/services/__tests__/bdd/xxx.steps.ts
    • Organize capabilities with English method names:
      given* / when* / then*
    • Scenario layer only calls methods from the step layer
  3. Fixture/Adapter Layer
    • src/backend/application/services/__tests__/bdd/xxx.fixture.ts
    • Unified management of mocks, fixtures, dependency injection and underlying calls

工具类约定

Utility Class Conventions

复用:
src/test/bdd.ts
推荐导入方式(避免 ESM
then
命名导出陷阱):
ts
import bdd from '@/test/bdd'
const { scenario, given, when, then } = bdd
Reuse:
src/test/bdd.ts
Recommended import method (to avoid ESM
then
named export pitfalls):
ts
import bdd from '@/test/bdd'
const { scenario, given, when, then } = bdd

标准落地流程

Standard Implementation Process

1) 先写场景清单(业务句子)

1) First write the scenario list (business sentences)

每条场景必须是一句中文:
给定<前置条件>,当<触发动作>,那么<业务结果>
Each scenario must follow the structure:
Given <precondition>, When <trigger action>, Then <business result>

2) 定义步骤词典方法

2) Define step dictionary methods

使用英文命名,示例:
  • givenInvalidProviderValues()
  • whenQueryEngineSelection()
  • thenProvidersNormalizedToNone()
规则:
  • given*
    只做前置状态准备
  • when*
    只触发行为
  • then*
    只做业务结果断言
Use English naming, examples:
  • givenInvalidProviderValues()
  • whenQueryEngineSelection()
  • thenProvidersNormalizedToNone()
Rules:
  • given*
    only prepares preconditions
  • when*
    only triggers actions
  • then*
    only asserts business results

3) 在实现层补齐 fixture

3) Complete fixtures in the implementation layer

实现层负责:
  • service 实例创建
  • store/mock 初始化
  • spy 管理(如
    fs.existsSync
  • 数据读写 helper(
    setValue/getValue
The implementation layer is responsible for:
  • Service instance creation
  • Store/mock initialization
  • Spy management (e.g.,
    fs.existsSync
    )
  • Data read/write helpers (
    setValue/getValue
    )

4) 场景层只编排,不实现

4) Scenario layer only orchestrates, does not implement

场景层示例:
ts
scenario('设置:引擎选择行为', () => {
  it('给定 provider 值非法,当查询引擎选择,那么应归一化为 none', async () => {
    await given('给定:provider 值非法', () => steps.givenInvalidProviderValues())
    await when('当:查询引擎选择', () => steps.whenQueryEngineSelection())
    await then('那么:应归一化为 none', () => steps.thenProvidersNormalizedToNone())
  })
})
Example of scenario layer:
ts
scenario('Settings: Engine Selection Behavior', () => {
  it('Given invalid provider values, when querying engine selection, then should normalize to none', async () => {
    await given('Given: invalid provider values', () => steps.givenInvalidProviderValues())
    await when('When: query engine selection', () => steps.whenQueryEngineSelection())
    await then('Then: should normalize to none', () => steps.thenProvidersNormalizedToNone())
  })
})

评审清单(8项)

Review Checklist (8 Items)

  • 场景名是“给定/当/那么”中文句式
  • 场景层无技术细节(无 mock/new/spyon)
  • 步骤方法命名统一
    given/when/then
    前缀
  • 步骤层不堆叠复杂 mock 逻辑
  • 实现层集中管理依赖和替身
  • Then 断言面向业务结果,不是私有实现
  • 场景间状态隔离(beforeEach 重置)
  • yarn test:bdd
    可通过
  • Scenario names follow the "Given-When-Then" sentence structure
  • No technical details in the scenario layer (no mock/new/spyOn)
  • Step method names uniformly use the
    given/when/then
    prefix
  • Step layer does not stack complex mock logic
  • Implementation layer centrally manages dependencies and test doubles
  • Then assertions focus on business results, not private implementations
  • State isolation between scenarios (reset via beforeEach)
  • Passes when running
    yarn test:bdd

常见误用(必须避免)

Common Misuses (Must Avoid)

  1. 在场景层直接写 mock 或访问 store
  2. 步骤方法名中英混用(触发命名规则告警)
  3. 一个场景覆盖多个业务承诺
  4. 只断言内部调用次数,不断言业务结果
  5. 步骤层直接包含大量底层构造逻辑(应下沉实现层)
  1. Directly writing mocks or accessing the store in the scenario layer
  2. Mixing Chinese and English in step method names (triggers naming rule alerts)
  3. One scenario covers multiple business commitments
  4. Only asserting internal call counts, not business results
  5. Step layer directly contains a large amount of underlying construction logic (should be delegated to the implementation layer)

运行命令

Run Commands

bash
yarn test:bdd
yarn test:bdd:watch
bash
yarn test:bdd
yarn test:bdd:watch

扩展建议

Extension Suggestions

  • 新业务域按同样三层创建:
    • xxx.bdd.test.ts
    • bdd/xxx.steps.ts
    • bdd/xxx.fixture.ts
  • 优先复用
    src/test/bdd.ts
    ,不要重复造 DSL。
  • Create new business domains following the same three-layer structure:
    • xxx.bdd.test.ts
    • bdd/xxx.steps.ts
    • bdd/xxx.fixture.ts
  • Prioritize reusing
    src/test/bdd.ts
    , do not reinvent the DSL.