mcpField
B
backend-test-creator

backend-test-creator

github.com/datex测试与质量92 次安装
代码仓库

关于 backend-test-creator

backend-test-creator 是用于 Datex Studio 的技能,可在分支上创建或修改后端测试套件。它围绕 Mocha 生命周期钩子和测试用例组织流程,并通过 CLI 验证后提交配置。

主要功能

  1. 01创建或修改后端测试套件
  2. 02配置四种 Mocha 生命周期钩子
  3. 03通过测试用例流程组织断言
  4. 04管理套件级共享状态
  5. 05验证并提交配置 JSON

适用场景

  1. 01为流程或操作编写确定性测试
  2. 02将测试逻辑从操作迁移到测试套件
  3. 03在分支上更新现有后端测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/datex/skills --skill backend-test-creator

安装后在 Claude Code 中输入/backend-test-creator确认已加载。

附带文件evals/evals.jsonreferences/backend-tests.md

详细介绍

后端测试创建器

在分支上创建或修改 Datex Studio 后端测试(configurationTypeId=24)——这是一个 mocha 测试套件,包含四个生命周期钩子(before_suite / after_suite / before_each / after_each)、一个 testCaseFlows[] 数组(每个条目都包装为一个 mocha it()),以及 $test 运行时全局变量上的套件级状态。后端测试在函数层级运行;通过 FootprintApi.extendedActions 桥接来调用操作。

参考资料

依赖项

  • requirements-gathering 技能——如果对话上下文中尚无需求简报,则调用它来生成需求简报
  • action-creator / function-creator 技能——当被测流程或仅用于测试的包装流程尚未出现在分支上时调用
  • type-definition-creator 技能——当 vars[] 条目引用的接口或联合类型尚未出现在分支上时调用
  • impact-analysis 技能——将作为测试的操作重构出去时调用(只有在确认没有其他内容调用该操作后才删除它),或修改仅用于测试的包装流程签名时调用

CLI 生命周期

后端测试的创建通过 dxs configuration 进行——这是适用于所有平台配置类型的通用 CRUD 基元。没有 dxs backendtest 子命令,也不支持字段级修补;你需要构建(或获取并提取)完整的 JSON 请求体,编辑后再将整个请求体推回去。CLI 中的类型标识符是 backendtest(小写,与 ConfigurationEndpoints.normalize_type 的输出一致),而不是 backendTest。

创建新的后端测试:

# 1. Build body.json from scratch (see references/backend-tests.md → Minimal Valid Skeleton)
# 2. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate backendtest -b <branchId> -D body.json
# 3. Create
dxs configuration upsert backendtest -b <branchId> -D body.json

编辑现有后端测试:

# 1. Fetch — note the envelope wrapper
dxs configuration get backendtest <configId> -b <branchId> -O envelope.json
# 2. EXTRACT THE INNER BODY (round-trip footgun guard — see "Round-trip rule" below)
jq .json envelope.json > body.json
# 3. Edit body.json
# 4. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate backendtest -b <branchId> -D body.json
# 5. Push
dxs configuration upsert backendtest -b <branchId> -D body.json
往返规则(至关重要)

编辑现有配置时,绝不要将 envelope.json 直接传入 dxs configuration upsert——这样会悄无声息地破坏配置内容。上文修正后的流程(先用 jq 提取内部 .json,再进行编辑)是所有往返操作的必经步骤。有关规范的往返流程和底层错误,请参阅 ../datex-studio-shared/configuration-roundtrip.md。

后端测试 JSON 必须是单行压缩格式(与 -customType.json 使用相同约定)——推送时不要美化格式。如果为方便编辑而在本地美化了格式,请在运行 dxs configuration upsert 前重新压缩。在 code 字符串中,换行符为 \r\n(在 JSON 层中转义为 \\r\\n);编辑嵌入的代码时,优先使用 Python 的 json.load / json.dump,而不要直接替换原始字符串。

工作流

[Phase 1: Setup + Requirements]
Follow branch-setup.md for branch/connection selection
        |
[requirements brief in context?]
  +-----+-----+
  |            |
 YES          NO -> invoke `requirements-gathering`
  |            |
  +-----+------+
        |
[Phase 2: Decide test scope]
Consult references/backend-tests.md → "Purpose & When to Use"
and "Test Case Layout":
  - test suite with deterministic cases -> backend-test
  - pure transform, no pass/fail -> helper function, stop here
  - live smoke check -> throwaway $flows.* call, stop here
Pick layout: one-flow-per-test (≤ ~25 cases) vs
one-flow-per-category (≥ ~30 cases); ambiguous 25-30 -> ask user
Action-as-test refactor? -> plan to delete the source action
after the suite lands; invoke `impact-analysis` before delete
        |
[Phase 3: Author test body]
Build body.json:
  - File shape (configurationTypeId=24, suffix -backendTest.json,
    single-line minified, referenceName==title, _test stem)
  - Four hook flows: before_suite / after_suite / before_each /
    after_each (referenceNames EXACT — renaming silently unbinds)
  - testCaseFlows[]: one flow per test (or per category)
  - $test.vars seeded in before_suite; never $flow.vars
  - Actions reached via $apis.<Package>.FootprintApi
    .extendedActions.<action_name>(...) — NOT $flows.*
  - Assertions hand-rolled (no chai/sinon/assert at runtime);
    throw Error(message) with input context in the message
  - Mocking strategy: prefer test-only wrapper flow; tolerate
    mock_data param only on code already shaped that way; flag
    runtime-mock seam as Platform TODO in before_suite comments
  - Invoke `action-creator` / `function-creator` if the
    flow-under-test or wrapper doesn't exist yet
        |
[Phase 4: Validate + push]
dxs configuration validate backendtest -b <branchId> -D body.json
        |
   +----+----+
   |         |
  CREATE   MODIFY-EXISTING
   |         |
   |         use the corrected round-trip
   |         (get -O envelope -> jq .json -> body)
   |         |
   +----+----+
        |
        v
dxs configuration upsert backendtest -b <branchId> -D body.json
   (upsert creates or updates by referenceName — one command for both)
        |
[Phase 5: Run the suite (optional)]
Trigger the mocha runner against the suite on the branch.
Watch for: hook never fires (referenceName drift), test code in
flows[] instead of testCaseFlows[] (silently ignored),
vacuously-passing "no X" assertions against fields the code no
longer produces.
        |
[invoke `post-edit-verification`; then `component-validator`]

阶段详情

阶段 1:准备 + 需求
  1. 按照 ../datex-studio-shared/branch-setup.md 选择分支和连接。绝不要假设分支 ID——请让用户确认。
  2. 检查对话上下文中是否已有需求简述(由 requirements-gathering 或其他调用技能生成)。
    • 已有简述——使用该简述。对于后端测试,简述应指出要测试的流程或操作、用于确保覆盖率的确定性用例、所需的夹具/模拟,以及这是全新测试套件还是对现有 action-as-test 的重构。
    • 没有简述——先调用 requirements-gathering 技能。“这真的是测试套件,还是只是冒烟检查?”这个问题对阶段 2 至关重要。
阶段 2:确定测试范围

编写测试前,请参阅 references/backend-tests.md → Purpose & When to Use。

如果工作内容是一个测试套件——通过多个用例、基线不变量、设置/清理和通过/失败报告来测试某个流程或操作——请选择后端测试。

  • 绝不要编写新的 action-as-test。 此组件出现之前,工程师会把测试套件塞进某个操作的 code 字符串中,并用手写的运行器执行。现在不再采用这种模式。遇到这类操作时(文件名通常以 test_ 开头或以 _test_action 结尾),应计划将其重构为后端测试——在此编写新套件,然后仅在调用 impact-analysis 技能确认没有其他内容调用该操作后,才删除源操作。
  • 如果逻辑是纯转换,不涉及通过/失败的概念,优先使用辅助函数,而不是后端测试——函数不会让你承担一个用不上的 mocha 运行器。
  • 不要将后端测试用作实时数据的冒烟检查工具。 后端测试用于确定性测试套件。如果你需要确认“这个流程在生产环境中是否会返回合理的结果?”,应从临时操作手动调用 $flows.*,而不是编写后端测试。

从 references/backend-tests.md → Minimal Valid Skeleton 中选择测试用例布局:

  1. 每个逻辑测试使用一个流程——这是最符合 mocha 惯例的形式。适用于套件不超过约 25 个用例,或需要在 mocha 输出中筛选单个失败用例的情况。
  2. 每个类别使用一个流程,在流程内部遍历用例——code 字符串定义用例对象数组并循环处理。适用于每个测试一个流程会导致流程数超过约 30 个,或同一类别中的用例共享足够多的设置、值得放在一起的情况。合理的结构是约 15–20 个类别,每个类别约 2–5 个用例。

如果预计用例数落在含糊的 25–30 区间,或用户表示希望新套件与该功能中现有套件的粒度保持一致,请询问用户。

阶段 3:编写测试主体

根据 references/backend-tests.md → Minimal Valid Skeleton 中的骨架构建 body.json。要点如下:

  1. 文件基础信息。 遵循下方的 预检清单 和 ../datex-studio-conventions/universal-checklist.md;有关 -backendTest.json 文件格式(以及 _test 文件名规则),请参阅 references/backend-tests.md → 文件位置与命名。

  2. flows[] 中必须恰好有四个钩子流程。 ReferenceName 为 before_suite / after_suite / before_each / after_each——拼写必须完全一致,不得使用变体。根级别的 beforeFlowConfig.flowId / afterFlowConfig.flowId / beforeEachFlowConfig.flowId / afterEachFlowConfig.flowId 会通过 referenceName(而非数字 id)解析到这四个流程之一。如果钩子流程的 referenceName 是 beforeSuite 或 setup,运行器会静默地不绑定它——钩子不会触发,用户却以为它已经生效。

    referenceNameMocha 钩子典型内容
    before_suitebefore()在 $test.vars 上初始化套件级状态:断言辅助函数、模拟对象构建器、开销较大的一次性固定数据。
    after_suiteafter()释放 before_suite 持久化的所有内容。通常为空。
    before_eachbeforeEach()重置每个测试的状态(计数器、临时集合),避免用例之间相互影响。
    after_eachafterEach()清理每个测试的数据。通常为空。
  3. 测试用例必须放在 testCaseFlows[] 中。 每个条目都是一个流程,其唯一步骤的 executeCodeConfig.code 是一个 it() 的主体。该流程的 referenceName 会成为 Mocha 输出中的测试名称。flows[] 中除四个钩子外的任何内容都会被运行器静默忽略——新手常因把测试用例放入错误的数组而导致静默失败。每个测试用例的 code 默认脚手架采用 Arrange / Act / Assert 模板:

    // Arrange
    
    // Act
    
    // Assert
    
  4. vars[] 是套件级状态机制,可通过 $test 运行时全局变量($test.vars.<id>)从任意钩子或测试用例代码中访问。后端测试拥有独立于 $flow 的运行时全局变量——运行器会在每次钩子和测试用例执行时填充 $test,而 $test.vars 会在整个套件生命周期内持久存在(before_suite → before_each + 用例 + after_each 循环 → after_suite)。每个 var 都是一个属性描述符,其格式与接口属性相同——请参阅 ../type-definition-creator/references/type-definitions.md。存储在 vars 上的函数必须标注为 object 类型(描述符架构中没有 function 基本类型)。

  5. 遵守 code 字符串中的层级要求。 后端测试运行在 函数层级,因此适用函数层级调用矩阵:

    • $test.vars.<id> — 在 vars[] 中声明的套件级状态。用于替代 $flow.vars——在后端测试中写入 $flow.vars 无法解析。
    • $apis.<Package>.FootprintApi.extendedActions.<action_name>({ ... }) — 调用一个操作。**这是从后端测试中调用操作的唯一方式。**这里不支持操作 → 操作语法 $flows.<Package>.<action_name>。
    • $flows.<Package>.<function_name>({ ... }) — 调用另一个函数。此层级的 $flows 只能访问函数。
    • $datasources.<Package>.<name>.get({ ... }) — 仅限函数层级的数据源(-datasource.json)。无法访问 FPDS(-footprintDatasource.json)。
    • $db.<Package>.<storage> — **不可用。**Studio Validate 会拒绝后端测试钩子/测试用例代码中的 $db,并报错 Cannot find name '$db'(已于 2026-07-15 验证——一个使用 $db 的套件,其流程可追溯到六月,却在首次上传时失败),尽管其他函数层级服务都能解析。存储访问必须通过同一包中的 $flows:如果测试本来就在验证契约,优先使用该功能真正的管理/动词流程;对于直接访问测试数据(插入旧版行、断言列状态、清理数据),请添加一个小型函数层级测试支持垫片,并在测试中调用它(已发布的先例:LaborManagement.task_control_engine_test 完全不使用 $db;SalesOrders.oa_test_support_flow 是垫片模式)。../db-query/references/db.md 中的规则适用于垫片内部。

    请参阅 ../datex-studio-runtime/runtime-globals.md 和 ../datex-studio-runtime/calling-conventions.md。

  6. 断言契约是出错即抛出。默认不会加载任何断言库——没有 expect、assert、chai 或 sinon。测试用例的代码正常返回即为通过,抛出异常即为失败。运行器会在 mocha 输出中显示 Error.message,因此请认真编写错误消息——“预期 3 个分配项,实际得到 2 个(lotIds = [1, 2])”远胜于“断言失败”。

    在 before_suite 中定义一次辅助函数,并将其存放在 $test.vars 上:

    // before_suite code
    $test.vars.assertEquals = (description, expected, actual) => {
      if (JSON.stringify(expected) !== JSON.stringify(actual)) {
        throw new Error(`${description}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
      }
    };
    
    $test.vars.buildMockData = (overrides) => { /* ... */ };
    

    每个测试用例都可以复用这些辅助函数。调用被测操作时,需通过 FootprintApi.extendedActions 桥接:

    // test case code
    const input = { context: 9, request: {...}, mock_data: $test.vars.buildMockData({...}) };
    const output = await $apis.Allocations.FootprintApi.extendedActions.plan_inventory_consumption_action(input);
    
    $test.vars.assertEquals('result count', 2, output.result.length);
    $test.vars.assertEquals('first lot id', 1, output.result[0].lot_id);
    
  7. **测试用例中的错误传递。**当测试用例调用的流程本身抛出异常时,mocha 会显示该错误。这通常正是你想要的结果——除非要将某种特定失败模式转换成更清晰的断言错误,否则不要用 try/catch 包裹调用。如果确实捕获异常,务必添加上下文后重新抛出(throw new Error(\calling plan_inventory_consumption_action with context=9 failed: ${e.message}`)`)——在测试用例中吞掉错误,在 mocha 输出中与“代码已正常运行”无法区分,这是最糟糕的假通过。

    钩子也适用相同规则,但有一点不同:钩子内部抛出异常会中止整个套件(mocha 的默认行为)。如果需要非致命失败,请在钩子内部吞掉异常,并将其记录到 $test.vars,以便之后由测试用例显示。

  8. 模拟策略。这里存在平台能力缺口:目前没有运行时切入点可拦截测试中的 $apis/$flows/$datasources/$db 调用。这意味着后端测试无法像使用 sinon 或 nock 那样模拟 FootprintApi 响应。要向被测流程提供预置数据,唯一方法是将数据接入流程。以下分为三个层级,从优到劣:

    1. 在运行时拦截 FootprintApi / $flows — 目前不可用。 这是平台应努力实现的方向。在 before_suite 注释中标记为 Platform TODO,参见 references/backend-tests.md → Platform TODOs。
    2. 仅用于测试的包装工作流。 编写一个明确接收模拟数据结构的工作流,并使用生产工作流的真实契约调用它。测试调用包装工作流;生产代码调用真实工作流。这是新工作的正确选择。 调用 function-creator 来编写包装工作流。
    3. 在生产工作流上添加 mock_data 参数。 既有操作有时会带有此参数——这会永久污染生产契约。仅将其视为最后手段,且只适用于已经采用这种形式的代码。绝不要在新的生产工作流上添加 mock_data 参数。 如果测试新工作流的唯一方法是预先为模拟数据做准备,请提出异议,并改为按(2)构建仅用于测试的包装工作流。
  9. 将操作重构为测试。 如果你要将操作形式的测试(文件名通常以 test_ 开头或以 _test_action 结尾)转换为后端测试:

    a. 首先编写新的 *-backendTest.json 套件;将操作 code 字符串中的测试用例复制到单独的 testCaseFlows[] 条目中(把手写的迭代改写为 mocha 用例;循环由运行器处理)。 b. 将套件级辅助函数从操作的 setup 块移到 before_suite,并将它们存放在 $test.vars 上。 c. 将任何 $flows.<Package>.<action>(...) 调用改写为 $apis.<Package>.FootprintApi.extendedActions.<action>(...)——层级从操作变为函数。 d. 推送新套件并确认它可以运行(阶段 5)。 e. 删除源操作前,调用 impact-analysis 技能,确认没有其他内容调用它。这个操作表面上可能只是个虚假的测试,但某处的真实调用方可能已经开始依赖它来执行生产工作——不要等删除后才发现。

阶段 4:验证并推送
# Validate the body locally against the branch. Exit 1 = validation found errors
# (read validation_errors, fix body.json, re-run) — not a broken CLI. Do not push on exit 1.
dxs configuration validate backendtest -b <branchId> -D body.json

# For a new backend test
dxs configuration upsert backendtest -b <branchId> -D body.json

# For modify-existing (round-trip — never skip the jq extract)
dxs configuration get backendtest <configId> -b <branchId> -O envelope.json
jq .json envelope.json > body.json
# ... edit body.json ...
dxs configuration upsert backendtest -b <branchId> -D body.json

验证会在推送前发现缺失的必填字段、vars[] 中格式错误的属性描述符结构,以及引用错误。它不会发现静默绑定失败(钩子工作流的 referenceName 错误)、将测试代码放在 flows[] 而不是 testCaseFlows[] 中,或断言恒通过等问题——这些属于运行时问题,应通过阶段 5 的验证以及下文的 references/backend-tests.md → Pre-Flight Checklist 进行审查。

阶段 5:运行套件(可选)

如果平台提供了在当前分支上触发 mocha 运行器来执行套件的方式(Studio 按钮、CLI 子命令,或对已知测试入口点的手动 $flows.* 调用),请运行并确认:

  • 四个钩子确实触发(重命名后的钩子,例如 beforeSuite,会静默地无法绑定;测试用例会运行,但缺少 setup)。
  • 每个测试用例都会在 mocha 输出中作为独立的 it() 显示(或按类别显示,具体取决于阶段 2 选择的布局)。四个钩子之外的 flows[] 中的任何内容都会被静默忽略——如果输出中“缺少”某个测试用例,原因就在这里。
  • 不存在断言恒通过的“没有 X”测试——如果测试针对已不再包含 foo 的输出断言 (result.foo ?? []).length === 0,那么每次回归都会通过。通过同时断言正向对照中存在该字段来审查。

如果无法访问运行器,请重新获取配置(使用修正后的 jq .json 提取模式),并与 body.json 比较差异,以确认推送已生效;四个钩子绑定和 testCaseFlows[] 布局应与编写内容一致。

提交前检查清单

推送前,请完整检查 references/backend-tests.md 中的清单。快速版:

  1. 文件基础信息。 后缀为 -backendTest.json,configurationTypeId: 24,referenceName 以 _test 结尾——还要通过通用检查(../datex-studio-conventions/universal-checklist.md)。
  2. flows[] 中必须恰好有四个钩子流程,其 referenceNames 分别为 before_suite / after_suite / before_each / after_each。每个根级的 beforeFlowConfig.flowId / afterFlowConfig.flowId / beforeEachFlowConfig.flowId / afterEachFlowConfig.flowId 都必须通过 referenceName 解析到其中一个流程。钩子名称错误会导致它静默地不触发。
  3. 测试用例应放在 testCaseFlows[] 中,而不是 flows[] 中。 flows[] 中除四个钩子以外的任何内容都会被运行器静默忽略。
  4. vars[] 描述符必须完整。 每个变量都包含 id、type、isCollection、objectType、isSecured;必需变量要添加 required: true;联合类型要包含 oneOf;常量要包含 isConstant: true + constantValue。
  5. code 字符串的换行符必须为 \r\n。 在解码后的字符串中如此(JSON 层会将其转义为 \\r\\n)。请使用 Python 的 json.load / json.dump 进行编辑——直接替换原始文本会破坏转义。
  6. 不要假定存在 expect / chai / sinon。 失败时抛出异常。手写断言辅助函数放在 $test.vars 上,并在 before_suite 中定义。
  7. 不要在生产流程中新增 mock_data 参数。 如果测试套件需要模拟数据,优先使用仅供测试的包装流程。只有原本已有 mock_data 的代码才可以保留该参数;请在测试套件的 before_suite 注释中标明,并附上指向 Platform 中关于运行时拦截的 TODO 的链接。
  8. 测试用例布局遵循第 2 阶段的决策规则: 少于约 25 个用例时,每个测试对应一个流程;多于约 30 个时,每个类别对应一个流程;介于两者之间时,情况不明确。
  9. 断言消息要包含输入信息。 assertEquals('allocation order', [1, 2], order) 会报告上下文;assertEquals('x', expected, actual) 则不会。
  10. 不要编写恒为真的“没有 X”断言。 如果输出中已不存在 foo,对 (result.foo ?? []).length === 0 的断言将永远得到 0 === 0。依赖缺失检查前,请先验证该字段路径仍然有效(正向控制)。

常见错误

错误修正方法
将 Hook 流命名为 beforeSuite / setup / before,而不是 before_suite静默解绑——运行器会通过 referenceName 解析 beforeFlowConfig.flowId;名称不匹配意味着 Hook 永远不会触发,但不会引发错误。请使用确切名称 before_suite / after_suite / before_each / after_each。
将测试用例放在 flows[] 而不是 testCaseFlows[] 中运行器只会将 testCaseFlows[] 包装为 mocha it()。四个 Hook 之外的任何内容若放在 flows[] 中,都会被静默忽略。
在测试用例中通过 $flows.<Package>.<action_name>(...) 调用 Action后端测试在函数层级运行;这种语法仅适用于 Action→Action。请改用 $apis.<Package>.FootprintApi.extendedActions.<action_name>({ ... })。
在 Hook 或测试用例代码中使用 $flow.vars 而不是 $test.vars后端测试有专用的运行时全局变量。$flow 无法解析。请将每处引用替换为 $test.vars.<id>。
认为 expect、chai、sinon 或 Node 内置的 assert 可用默认不会加载这些库。请在 before_suite 中手动编写辅助函数,在失败时执行 throw new Error(...);并将它们存放在 $test.vars 上。
为了让后端测试能够模拟新生产流,给它添加 mock_data 参数这会永久污染生产流的契约。请改为构建仅用于测试的包装流(调用 function-creator)。只有原本就采用这种结构的代码才允许使用 mock_data 参数;新代码绝不添加这类参数。
在测试用例中吞掉错误,以便“让测试套件继续运行”在 mocha 输出中,这与通过无法区分——这是最糟糕的一类误报。如果捕获了错误,请附上上下文后重新抛出。
推送前美化 JSON 格式按平台约定,后端测试文件必须是单行压缩格式(与 -customType.json 相同)。请在执行 dxs configuration upsert 前重新压缩。
将 dxs configuration get -O envelope.json 的输出直接传给 dxs configuration upsert -D envelope.json这会静默破坏配置内容。编辑前务必先执行 jq .json envelope.json > body.json。请参阅上文的“往返规则”。
未检查调用方就删除作为测试的 Action该 Action 可能不只用于自测。删除前请调用 impact-analysis。
针对已不再包含 foo 的输出断言 (result.foo ?? []).length === 0这会让所有回归问题都能侥幸通过。请添加正向检查,在已知良好的用例中断言 foo 存在;或者直接断言预期值,而不是断言其不存在。
description 超过 100 个字符SQL 列长度有限,推送将无法通过验证。请缩短描述。
referenceName ≠ title,或文件名主体与 referenceName 不匹配查找结果 / mocha 输出标签会不正确。按照约定,这三个名称应一致:文件名主体 == referenceName == title,且都以 _test 结尾。

编辑后,请调用 post-edit-verification,以发现描述、JSON 或架构违规。最终审查时,请调用 component-validator。

评论

支持 Markdown