OpenClaw 插件测试指南:SDK 测试工具、契约测试与 lint 约束全解析
OpenClaw 插件测试指南SDK 测试工具、契约测试与 lint 约束全解析【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 项目地址: https://gitcode.com/GitHub_Trending/cl/openclawOpenClaw 插件 SDK 为内置bundled插件提供了覆盖单元测试、契约测试与运行时模拟的完整测试基础设施。本文以 sdk-testing.md 参考文档为主线系统讲解openclaw/plugin-sdk/*测试子路径的导入规范、各类测试工具与类型导出、通道/提供商插件的推荐测试模式、仓库内置插件的契约测试与 lint 边界约束并结合仓库源码src/plugin-sdk/、src/plugins/contracts/印证底层实现帮助你在编写插件测试时快速选型、正确导入并避免常见误区。测试子路径聚焦式导入替代旧 barrelOpenClaw 插件 SDK 的测试工具并非通过单一的openclaw/plugin-sdk/testing大杂烩入口暴露而是拆分为一系列聚焦子路径focused subpaths。这些子路径是仓库内部的源码入口repo-local source entrypoints专门服务于 OpenClaw 自身内置插件的测试并非对外发布的package.jsonexports第三方插件不能依赖它们它们可以自由导入 Vitest 或其他仅存在于仓库内的测试依赖。一个典型的测试导入集合如下import { shouldAckReaction, removeAckReactionAfterReply, } from openclaw/plugin-sdk/channel-feedback; import { installCommonResolveTargetErrorCases } from openclaw/plugin-sdk/channel-target-testing; import { AUTH_PROFILE_RUNTIME_CONTRACT } from openclaw/plugin-sdk/agent-runtime-test-contracts; import { createTestPluginApi } from openclaw/plugin-sdk/plugin-test-api; import { expectChannelInboundContextContract } from openclaw/plugin-sdk/channel-contract-testing; import { createStartAccountContext } from openclaw/plugin-sdk/channel-test-helpers; import { describePluginRegistrationContract } from openclaw/plugin-sdk/plugin-test-contracts; import { registerSingleProviderPlugin } from openclaw/plugin-sdk/plugin-test-runtime; import { describeOpenAIProviderRuntimeContract } from openclaw/plugin-sdk/provider-test-contracts; import { getProviderHttpMocks } from openclaw/plugin-sdk/provider-http-test-mocks; import { createOpenClawTestState } from openclaw/plugin-sdk/test-state; import { withEnv, withFetchPreconnect, withServer } from openclaw/plugin-sdk/test-env; import { isLiveTestEnabled } from openclaw/plugin-sdk/test-live; import { createRequestCaptureJsonFetch } from openclaw/plugin-sdk/test-media-understanding; import { bundledPluginRoot, createCliRuntimeCapture, runDirectImportSmoke, typedCases, } from openclaw/plugin-sdk/test-fixtures; import { mockNodeBuiltinModule } from openclaw/plugin-sdk/test-node-mocks;这些代码对应的实现文件位于仓库的 src/plugin-sdk/ 目录下包括plugin-test-api.ts、channel-test-helpers.ts、provider-test-contracts.ts、test-env.ts、test-state.ts等可通过 packages/plugin-sdk 与 src/plugin-sdk 两处目录对照查看。注意历史变更此前仓库存在openclaw/plugin-sdk/testingbarrel 入口和openclaw/plugin-sdk/test-utils别名二者均为仓库本地使用、未随包发布现已被移除。编写新测试时务必改用上表中的聚焦子路径。pnpm run lint:plugins:no-extension-test-core-imports实现见 scripts/check-no-extension-test-core-imports.ts会在 CI 中强制扩展测试只使用这些受控子路径防止重新引入旧别名或越界导入核心测试助手。可用导出总表下表汇总了文档列出的全部测试工具导出、用途与推荐导入子路径供测试开发时快速查表导出用途导入子路径createTestPluginApi构建最小化插件 API mock用于直接注册的单元测试plugin-sdk/plugin-test-apiAUTH_PROFILE_RUNTIME_CONTRACT原生 Agent 运行时适配器共享的 auth-profile 契约 fixtureplugin-sdk/agent-runtime-test-contractsDELIVERY_NO_REPLY_RUNTIME_CONTRACT原生 Agent 运行时适配器共享的投递抑制契约 fixtureplugin-sdk/agent-runtime-test-contractsOUTCOME_FALLBACK_RUNTIME_CONTRACT原生 Agent 运行时适配器共享的 fallback 分类契约 fixtureplugin-sdk/agent-runtime-test-contractscreateParameterFreeTool为原生运行时契约测试构建动态工具 schema fixtureplugin-sdk/agent-runtime-test-contractsexpectChannelInboundContextContract断言通道入站上下文形状plugin-sdk/channel-contract-testinginstallChannelOutboundPayloadContractSuite安装通道出站负载契约用例plugin-sdk/channel-contract-testingcreateStartAccountContext构建通道账号生命周期上下文plugin-sdk/channel-test-helpersinstallChannelActionsContractSuite安装通用通道消息动作契约用例plugin-sdk/channel-test-helpersinstallChannelSetupContractSuite安装通用通道设置契约用例plugin-sdk/channel-test-helpersinstallChannelStatusContractSuite安装通用通道状态契约用例plugin-sdk/channel-test-helpersexpectDirectoryIds断言目录列表函数返回的通道目录 idplugin-sdk/channel-test-helpersformatEnvelopeTimestamp生成确定性的信封时间戳plugin-sdk/channel-test-helpersexpectPairingReplyText断言通道配对回复文本并抽取其中的 codeplugin-sdk/channel-test-helpersdescribePluginRegistrationContract安装插件注册契约检查plugin-sdk/plugin-test-contractsregisterSingleProviderPlugin在 loader 冒烟测试中注册单个提供商插件plugin-sdk/plugin-test-runtimeregisterProviderPlugin捕获单个插件的所有提供商类型plugin-sdk/plugin-test-runtimeregisterProviderPlugins跨多个插件捕获提供商注册信息plugin-sdk/plugin-test-runtimerequireRegisteredProvider断言提供商集合包含某个 idplugin-sdk/plugin-test-runtimecreateRuntimeEnv构建 mock 的 CLI/插件运行时环境plugin-sdk/plugin-test-runtimecreatePluginRuntimeMock构建 mock 的插件运行时表面plugin-sdk/plugin-test-runtimecreatePluginSetupWizardStatus为通道插件构建设置状态助手plugin-sdk/plugin-test-runtimecreateTestWizardPrompter构建 mock 的设置向导 prompterplugin-sdk/plugin-test-runtimecreateRuntimeTaskFlow创建隔离的运行时任务流状态plugin-sdk/plugin-test-runtimerunProviderCatalog以测试依赖执行提供商 catalog hookplugin-sdk/plugin-test-runtimeresolveProviderWizardOptions在契约测试中解析提供商设置向导选项plugin-sdk/plugin-test-runtimeresolveProviderModelPickerEntries在契约测试中解析提供商模型选择器条目plugin-sdk/plugin-test-runtimebuildProviderPluginMethodChoice构建提供商向导 choice id 用于断言plugin-sdk/plugin-test-runtimesetProviderWizardProvidersResolverForTest为隔离测试注入提供商向导 providersplugin-sdk/plugin-test-runtimedescribeOpenAIProviderRuntimeContract安装提供商家族运行时契约检查plugin-sdk/provider-test-contractsexpectPassthroughReplayPolicy断言提供商重放策略透传其自有工具与元数据plugin-sdk/provider-test-contractsrunRealtimeSttLiveTest使用共享音频 fixture 运行实时 STT 提供商 live 测试plugin-sdk/provider-test-contractsnormalizeTranscriptForMatch在模糊断言前规范化 live 转写输出plugin-sdk/provider-test-contractsexpectExplicitVideoGenerationCapabilities断言视频提供商声明显式生成模式能力plugin-sdk/provider-test-contractsexpectExplicitMusicGenerationCapabilities断言音乐提供商声明显式生成/编辑能力plugin-sdk/provider-test-contractsmockSuccessfulDashscopeVideoTask安装成功的 DashScope 兼容视频任务响应plugin-sdk/provider-test-contractsgetProviderHttpMocks访问可选的提供商 HTTP/auth Vitest mockplugin-sdk/provider-http-test-mocksinstallProviderHttpMockCleanup每个测试后重置提供商 HTTP/auth mockplugin-sdk/provider-http-test-mockscreateOpenClawTestState/withOpenClawTestState/OpenClawTestState创建并清理隔离的 OpenClaw state、配置、工作区、环境与 auth-profile fixtureplugin-sdk/test-stateinstallCommonResolveTargetErrorCases目标解析错误处理的共享测试用例plugin-sdk/channel-target-testingshouldAckReaction检查通道是否应添加 ack 反应plugin-sdk/channel-feedbackremoveAckReactionAfterReply在回复投递后移除 ack 反应plugin-sdk/channel-feedbackcreateTestRegistry构建通道插件注册表 fixtureplugin-sdk/plugin-test-runtime或plugin-sdk/channel-test-helperscreateEmptyPluginRegistry构建空插件注册表 fixtureplugin-sdk/plugin-test-runtime或plugin-sdk/channel-test-helperscreatePluginMetadataSnapshotFixture构建带对齐 manifest 与已安装插件视图的完整元数据快照plugin-sdk/plugin-test-runtimesetActivePluginRegistry为插件运行时测试安装注册表 fixtureplugin-sdk/plugin-test-runtime或plugin-sdk/channel-test-helperscreateRequestCaptureJsonFetch在媒体助手测试中捕获 JSON fetch 请求plugin-sdk/test-media-understandingisLiveTestEnabled门控可选的 live 提供商测试plugin-sdk/test-livecollectProviderApiKeys为 live 提供商测试发现凭据plugin-sdk/test-live-authparseProviderModelMap解析音乐/视频 live 测试模型覆盖plugin-sdk/test-media-generationwithServer针对一次性的本地 HTTP 服务器运行测试plugin-sdk/test-envcreateMockIncomingRequest构建最小化 HTTP 入站请求对象plugin-sdk/test-envwithFetchPreconnect安装预连接 hook 后运行 fetch 测试plugin-sdk/test-envwithEnv/withEnvAsync临时修补环境变量plugin-sdk/test-envcreateTempHomeEnv/withTempHome/withTempDir创建隔离的文件系统测试 fixtureplugin-sdk/test-envcreateMockServerResponse创建最小化 HTTP 服务端响应 mockplugin-sdk/test-envcreateProviderUsageFetch构建提供商 usage fetch fixtureplugin-sdk/test-envuseFrozenTime/useRealTime冻结与恢复计时器用于时间敏感测试plugin-sdk/test-envcreateCliRuntimeCapture在测试中捕获 CLI 运行时输出plugin-sdk/test-fixturesfindSourceImportBackedges异步检查仓库源码静态导入闭包中的禁用依赖plugin-sdk/test-fixturesrunDirectImportSmoke在隔离的 Node 进程中运行插件公共表面导入冒烟plugin-sdk/test-fixturesimportFreshModule以全新 query token 导入 ESM 模块以绕过模块缓存plugin-sdk/test-fixturesbundledPluginRoot/bundledPluginFile解析内置插件源码或 dist fixture 路径plugin-sdk/test-fixturesmockNodeBuiltinModule安装窄范围的 Node 内置模块 Vitest mockplugin-sdk/test-node-mockscreateSandboxTestContext构建沙箱测试上下文plugin-sdk/test-fixtureswriteSkill写入技能skillfixtureplugin-sdk/test-fixturesmakeAgentAssistantMessage构建 Agent 转写消息 fixtureplugin-sdk/test-fixturespeekSystemEvents/resetSystemEventsForTest检查与重置系统事件 fixtureplugin-sdk/test-fixturessanitizeTerminalText清理终端输出以便断言plugin-sdk/test-fixturescountLines/hasBalancedFences断言分块chunking输出形状plugin-sdk/test-fixturestypedCases为表驱动测试保留字面量类型plugin-sdk/test-fixtures内置插件的契约测试套件同样依赖这些 SDK 测试子路径中的测试专用注册表、manifest、公共产物与运行时 fixture 助手。而依赖内置插件清单bundled OpenClaw inventory的 core-only 测试套件则保留在src/plugins/contracts之下。特殊场景助手通道账号策略继承测试从openclaw/plugin-sdk/channel-test-helpers导入的createAccountPolicyInheritanceCases()每次调用返回四行字面量继承数据fresh objects and arrays保留被省略的策略字段。将其与validateTestChannelConfig(channelId, channelConfig)搭配使用——后者会通过宿主配置边界校验 schema 解析后的通道数据。每个插件测试仍需自行负责 schema 解析、账号解析器与断言包括断言被省略的账号策略继续保持缺省。零用量输入createZeroUsageFixture()来自openclaw/plugin-sdk/test-fixtures返回不含可选遥测字段的全新 usage 与嵌套 cost 对象。编写断言时应显式写出期望的 usage 值。测试可用的类型聚焦子路径同时 re-export 测试文件常用类型import type { ChannelAccountSnapshot, ChannelGatewayContext, } from openclaw/plugin-sdk/channel-contract; import type { OpenClawConfig } from openclaw/plugin-sdk/config-contracts; import type { MockFn, PluginRuntime, RuntimeEnv } from openclaw/plugin-sdk/plugin-test-runtime;PluginRuntime与RuntimeEnv类型在 src/plugin-sdk/plugin-test-runtime.ts 中定义并导出与运行时 mock 构造器配套使用。测试目标解析target resolution通道插件需要把to、mode、allowFrom等参数解析为实际的发送目标。为了不重复编写错误处理用例直接安装共享用例集installCommonResolveTargetErrorCasesimport { describe } from vitest; import { installCommonResolveTargetErrorCases } from openclaw/plugin-sdk/channel-target-testing; describe(my-channel target resolution, () { installCommonResolveTargetErrorCases({ resolveTarget: ({ to, mode, allowFrom }) { // Your channels target resolution logic return myChannelResolveTarget({ to, mode, allowFrom }); }, implicitAllowFrom: [user1, user2], }); // Add channel-specific test cases it(should resolve username targets, () { // ... }); });implicitAllowFrom声明该通道默认允许的账号集合用于校验隐式来源许可共享用例会覆盖缺失目标、非法模式、来源未授权等标准错误路径而通道特有的解析逻辑如username风格目标则在it块中自行补充。测试模式Testing patterns注册契约测试务必包含 loader-backed 冒烟测试只把手写的apimock 传给register(api)的单元测试不会经过 OpenClaw loader 的准入闸门。真实的 loader 在以下情况会拒绝插件注册缺少必需的元数据插件调用了它并不拥有的能力 API。例如api.registerHook(...)要求提供 hook 名称api.registerMemoryCapability(...)要求插件 manifest 或导出入口声明kind: memory。因此对于插件依赖的每个注册表面尤其是 hook 与 memory 这类独占能力至少补充一个 loader-backed 冒烟测试。createTestPluginApi实现见 src/plugin-sdk/plugin-test-api.ts用于构建 SDK 形状的宿主 API 对象可配合直接注册单元测试而 loader 路径的验证则依赖src/plugins/contracts/plugin-registration.contract.test.ts这类契约测试。运行时配置访问测试优先使用openclaw/plugin-sdk/plugin-test-runtime提供的共享插件运行时 mock。其运行时配置助手模拟了当前的快照与变更mutationAPI避免为每个测试手写config.current/config.mutateConfigFile等桩实现。通道插件单元测试import { describe, it, expect, vi } from vitest; describe(my-channel plugin, () { it(should resolve account from config, () { const cfg { channels: { my-channel: { token: test-token, allowFrom: [user1], }, }, }; const account myPlugin.setup.resolveAccount(cfg, undefined); expect(account.token).toBe(test-token); }); it(should inspect account without materializing secrets, () { const cfg { channels: { my-channel: { token: test-token }, }, }; const inspection myPlugin.setup.inspectAccount(cfg, undefined); expect(inspection.configured).toBe(true); expect(inspection.tokenStatus).toBe(available); // No token value exposed expect(inspection).not.toHaveProperty(token); }); });第二个用例体现了通道账号检查的关键安全语义inspectAccount只返回configured、tokenStatus这类元信息绝不把 token 明文暴露到检查结果中测试应显式断言结果对象不包含token属性。提供商插件单元测试import { describe, it, expect } from vitest; describe(my-provider plugin, () { it(should resolve dynamic models, () { const model myProvider.resolveDynamicModel({ modelId: custom-model-v2, // ... context }); expect(model.id).toBe(custom-model-v2); expect(model.provider).toBe(my-provider); expect(model.api).toBe(openai-completions); }); it(should return catalog when API key is available, async () { const result await myProvider.catalog.run({ resolveProviderApiKey: () ({ apiKey: test-key }), // ... context }); expect(result?.provider?.models).toHaveLength(2); }); });模拟插件运行时plugin runtime对于使用createPluginRuntimeStore的代码在测试中通过 store 注入 mock 运行时import { createPluginRuntimeStore } from openclaw/plugin-sdk/runtime-store; import type { PluginRuntime } from openclaw/plugin-sdk/runtime-store; const store createPluginRuntimeStorePluginRuntime({ pluginId: test-plugin, errorMessage: test runtime not set, }); // In test setup const mockRuntime { agent: { resolveAgentDir: vi.fn().mockReturnValue(/tmp/agent), // ... other mocks }, config: { current: vi.fn(() ({}) as const), mutateConfigFile: vi.fn(), replaceConfigFile: vi.fn(), }, // ... other namespaces } as unknown as PluginRuntime; store.setRuntime(mockRuntime); // After tests store.clearRuntime();PluginRuntime类型同样从 src/plugin-sdk/plugin-test-runtime.ts 导出测试文件可用import type引入确保 mock 形状与真实运行时对齐。注意在测试收尾时调用store.clearRuntime()恢复全局状态避免用例间相互污染。优先使用 per-instance stub 而非原型修改// Preferred: per-instance stub const client new MyChannelClient(); client.sendMessage vi.fn().mockResolvedValue({ id: msg-1 }); // Avoid: prototype mutation // MyChannelClient.prototype.sendMessage vi.fn();per-instance stub 作用域清晰、随实例生命周期释放不会泄漏到其他测试原型修改则会污染类所有实例且容易在测试顺序变化时产生诡异失败。内置插件的契约测试Contract tests仓库内置bundled插件通过契约测试验证注册归属与运行时合规pnpm test src/plugins/contracts/这些测试断言以下内容哪些插件注册了哪些提供商provider哪些插件注册了哪些语音提供商speech provider注册形状registration shape的正确性运行时契约的合规性。契约测试的具体实现集中在 src/plugins/contracts/ 目录包含shape.contract.test.ts注册形状、auth-choice.contract.test.ts鉴权选择、runtime-seams.contract.test.ts运行时接缝、plugin-registration.contract.test.ts注册准入以及providers.contract.test.ts、tts.contract.test.ts等文件可逐一对照阅读。运行范围化测试针对某个具体插件pnpm test bundled-plugin-root/my-channel/仅运行契约测试pnpm test src/plugins/contracts/shape.contract.test.ts pnpm test src/plugins/contracts/auth-choice.contract.test.ts pnpm test src/plugins/contracts/runtime-seams.contract.test.ts内置插件的 lint 约束scripts/run-additional-boundary-checks.mts 在 CI 中执行一组lint:plugins:*导入边界检查每项也可本地独立运行对应脚本位于 scripts/check-no-extension-test-core-imports.ts 等命令强制约束pnpm run lint:plugins:no-monolithic-plugin-sdk-entry-imports内置插件不得导入单体式的openclaw/plugin-sdk根 barrelpnpm run lint:plugins:no-extension-src-imports生产扩展文件不得直接导入仓库src/**树../../src/...pnpm run lint:plugins:no-extension-test-core-imports扩展测试文件不得导入已移除的 SDK 测试别名或其他 core-only 测试助手这三条命令已在仓库根 package.json 中登记lint:plugins:no-extension-src-imports、lint:plugins:no-extension-test-core-imports、lint:plugins:no-monolithic-plugin-sdk-entry-imports。外部插件不受这些 lint 规则约束但遵循同样的模式是被推荐的保持导入面聚焦、避免与仓库内部实现耦合有利于长期可维护性。测试配置OpenClaw 使用Vitest 5并启用信息性informational的 V8 覆盖率报告。常用命令# Run all tests pnpm test # Run specific plugin tests pnpm test bundled-plugin-root/my-channel/src/channel.test.ts # Run with a specific test name filter pnpm test bundled-plugin-root/my-channel/ -t resolves account # Run with coverage pnpm test:coverage若本地运行出现内存压力可限制 worker 数量OPENCLAW_VITEST_MAX_WORKERS1 pnpm test关联文档SDK 总览导入约定SDK 通道插件通道插件接口与 Step 6 测试示例SDK 提供商插件提供商插件 hooks 与 Step 6 测试示例插件构建入门从零开始构建插件【免费下载链接】openclawThe AI that really does things. Any OS. Any Platform. The lobster way. 项目地址: https://gitcode.com/GitHub_Trending/cl/openclaw创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考