測試公用程式
這些子路徑是 OpenClaw 自有內建外掛測試在存放庫本機使用的原始碼進入點。它們並非為第三方外掛發布的package.json 匯出,且可能會匯入 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 { 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,
typedCases,
} from "openclaw/plugin-sdk/test-fixtures";
import { mockNodeBuiltinModule } from "openclaw/plugin-sdk/test-node-mocks";
openclaw/plugin-sdk/testing 彙總入口僅供存放庫本機使用,未包含在發布的套件中,現已移除。先前的 openclaw/plugin-sdk/test-utils 別名也隨之移除。pnpm run lint:plugins:no-extension-test-core-imports(scripts/check-no-extension-test-core-imports.ts)會讓擴充功能測試繼續使用上述聚焦的測試子路徑。
可用的匯出
| 匯出項目 | 用途 |
|---|---|
createTestPluginApi | 建立最小化的外掛 API 模擬,用於直接註冊單元測試。從 plugin-sdk/plugin-test-api 匯入 |
AUTH_PROFILE_RUNTIME_CONTRACT | 原生代理程式執行階段介面卡共用的驗證設定檔契約測試資料。從 plugin-sdk/agent-runtime-test-contracts 匯入 |
DELIVERY_NO_REPLY_RUNTIME_CONTRACT | 原生代理程式執行階段介面卡共用的傳遞抑制契約測試資料。從 plugin-sdk/agent-runtime-test-contracts 匯入 |
OUTCOME_FALLBACK_RUNTIME_CONTRACT | 原生代理程式執行階段介面卡共用的後援分類契約測試資料。從 plugin-sdk/agent-runtime-test-contracts 匯入 |
createParameterFreeTool | 建立動態工具結構描述測試資料,用於原生執行階段契約測試。從 plugin-sdk/agent-runtime-test-contracts 匯入 |
expectChannelInboundContextContract | 斷言頻道傳入內容的形狀。從 plugin-sdk/channel-contract-testing 匯入 |
installChannelOutboundPayloadContractSuite | 安裝頻道傳出承載資料契約測試案例。從 plugin-sdk/channel-contract-testing 匯入 |
createStartAccountContext | 建立頻道帳號生命週期內容。從 plugin-sdk/channel-test-helpers 匯入 |
installChannelActionsContractSuite | 安裝通用頻道訊息動作契約測試案例。從 plugin-sdk/channel-test-helpers 匯入 |
installChannelSetupContractSuite | 安裝通用頻道設定契約測試案例。從 plugin-sdk/channel-test-helpers 匯入 |
installChannelStatusContractSuite | 安裝通用頻道狀態契約測試案例。從 plugin-sdk/channel-test-helpers 匯入 |
expectDirectoryIds | 從目錄清單函式斷言頻道目錄 ID。從 plugin-sdk/channel-test-helpers 匯入 |
assertBundledChannelEntries | 斷言隨附頻道進入點公開預期的公用契約。從 plugin-sdk/channel-test-helpers 匯入 |
formatEnvelopeTimestamp | 格式化具確定性的封套時間戳記。從 plugin-sdk/channel-test-helpers 匯入 |
expectPairingReplyText | 斷言頻道配對回覆文字並擷取其代碼。從 plugin-sdk/channel-test-helpers 匯入 |
describePluginRegistrationContract | 安裝外掛註冊契約檢查。從 plugin-sdk/plugin-test-contracts 匯入 |
registerSingleProviderPlugin | 在載入器冒煙測試中註冊一個供應商外掛。從 plugin-sdk/plugin-test-runtime 匯入 |
registerProviderPlugin | 從一個外掛擷取所有供應商種類。從 plugin-sdk/plugin-test-runtime 匯入 |
registerProviderPlugins | 擷取多個外掛的供應商註冊項目。從 plugin-sdk/plugin-test-runtime 匯入 |
requireRegisteredProvider | 斷言供應商集合包含某個 ID。從 plugin-sdk/plugin-test-runtime 匯入 |
createRuntimeEnv | 建立模擬的命令列介面/外掛執行階段環境。從 plugin-sdk/plugin-test-runtime 匯入 |
createPluginRuntimeMock | 建立模擬的外掛執行階段介面。從 plugin-sdk/plugin-test-runtime 匯入 |
createPluginSetupWizardStatus | 為頻道外掛建立設定狀態輔助工具。從 plugin-sdk/plugin-test-runtime 匯入 |
createTestWizardPrompter | 建立模擬的設定精靈提示器。從 plugin-sdk/plugin-test-runtime 匯入 |
createRuntimeTaskFlow | 建立隔離的執行階段 TaskFlow 狀態。從 plugin-sdk/plugin-test-runtime 匯入 |
runProviderCatalog | 使用測試相依項目執行供應商目錄鉤子。從 plugin-sdk/plugin-test-runtime 匯入 |
resolveProviderWizardOptions | 在契約測試中解析供應商設定精靈選項。從 plugin-sdk/plugin-test-runtime 匯入 |
resolveProviderModelPickerEntries | 在契約測試中解析供應商模型選擇器項目。從 plugin-sdk/plugin-test-runtime 匯入 |
buildProviderPluginMethodChoice | 建立供應商精靈選項 ID 以供斷言。從 plugin-sdk/plugin-test-runtime 匯入 |
setProviderWizardProvidersResolverForTest | 為隔離測試注入供應商精靈的供應商。從 plugin-sdk/plugin-test-runtime 匯入 |
describeOpenAIProviderRuntimeContract | 安裝供應商系列執行階段契約檢查。從 plugin-sdk/provider-test-contracts 匯入 |
expectPassthroughReplayPolicy | 斷言供應商重播原則會傳遞供應商自有的工具與中繼資料。從 plugin-sdk/provider-test-contracts 匯入 |
runRealtimeSttLiveTest | 使用共用音訊測試資料執行即時 STT 供應商實機測試。從 plugin-sdk/provider-test-contracts 匯入 |
normalizeTranscriptForMatch | 在模糊斷言前正規化實機轉錄輸出。從 plugin-sdk/provider-test-contracts 匯入 |
expectExplicitVideoGenerationCapabilities | 斷言影片供應商明確宣告生成模式功能。從 plugin-sdk/provider-test-contracts 匯入 |
expectExplicitMusicGenerationCapabilities | 斷言音樂供應商明確宣告生成/編輯功能。從 plugin-sdk/provider-test-contracts 匯入 |
mockSuccessfulDashscopeVideoTask | 安裝成功的 DashScope 相容影片任務回應。從 plugin-sdk/provider-test-contracts 匯入 |
getProviderHttpMocks | 存取選擇啟用的供應商 HTTP/驗證 Vitest 模擬。從 plugin-sdk/provider-http-test-mocks 匯入 |
installProviderHttpMockCleanup | 在每項測試後重設供應商 HTTP/驗證模擬。從 plugin-sdk/provider-http-test-mocks 匯入 |
installCommonResolveTargetErrorCases | 目標解析錯誤處理的共用測試案例。從 plugin-sdk/channel-target-testing 匯入 |
shouldAckReaction | 檢查頻道是否應新增確認反應。從 plugin-sdk/channel-feedback 匯入 |
removeAckReactionAfterReply | 回覆傳遞後移除確認反應。從 plugin-sdk/channel-feedback 匯入 |
createTestRegistry | 建立頻道外掛登錄測試資料。從 plugin-sdk/plugin-test-runtime 或 plugin-sdk/channel-test-helpers 匯入 |
createEmptyPluginRegistry | 建立空的外掛登錄測試資料。從 plugin-sdk/plugin-test-runtime 或 plugin-sdk/channel-test-helpers 匯入 |
setActivePluginRegistry | 為外掛執行階段測試安裝登錄測試資料。從 plugin-sdk/plugin-test-runtime 或 plugin-sdk/channel-test-helpers 匯入 |
createRequestCaptureJsonFetch | 在媒體輔助工具測試中擷取 JSON fetch 請求。從 plugin-sdk/test-media-understanding 匯入 |
isLiveTestEnabled | 控制選擇啟用的實機供應商測試。從 plugin-sdk/test-live 匯入 |
collectProviderApiKeys | 探索實機供應商測試的認證資訊。從 plugin-sdk/test-live-auth 匯入 |
parseProviderModelMap | 剖析音樂/影片實機測試的模型覆寫值。從 plugin-sdk/test-media-generation 匯入 |
withServer | 對可拋棄的本機 HTTP 伺服器執行測試。從 plugin-sdk/test-env 匯入 |
createMockIncomingRequest | 建立最小化的傳入 HTTP 請求物件。從 plugin-sdk/test-env 匯入 |
withFetchPreconnect | 執行已安裝預連線鉤子的 fetch 測試。從 plugin-sdk/test-env 匯入 |
withEnv / withEnvAsync | 暫時修補環境變數。從 plugin-sdk/test-env 匯入 |
createTempHomeEnv / withTempHome / withTempDir | 建立隔離的檔案系統測試資料。從 plugin-sdk/test-env 匯入 |
createMockServerResponse | 建立最小化的 HTTP 伺服器回應模擬。從 plugin-sdk/test-env 匯入 |
createProviderUsageFetch | 建立供應商用量 fetch 測試資料。從 plugin-sdk/test-env 匯入 |
useFrozenTime / useRealTime | 凍結並還原計時器,用於時間敏感的測試。從 plugin-sdk/test-env 匯入 |
createCliRuntimeCapture | 在測試中擷取命令列介面執行階段輸出。從 plugin-sdk/test-fixtures 匯入 |
importFreshModule | 使用新的查詢權杖匯入 ESM 模組,以略過模組快取。從 plugin-sdk/test-fixtures 匯入 |
bundledPluginRoot / bundledPluginFile | 解析隨附外掛的原始碼或 dist 測試資料路徑。從 plugin-sdk/test-fixtures 匯入 |
mockNodeBuiltinModule | 安裝範圍有限的 Node 內建 Vitest 模擬。從 plugin-sdk/test-node-mocks 匯入 |
createSandboxTestContext | 建立沙箱測試內容。從 plugin-sdk/test-fixtures 匯入 |
writeSkill | 寫入 Skills 測試資料。從 plugin-sdk/test-fixtures 匯入 |
makeAgentAssistantMessage | 建立代理程式轉錄訊息測試資料。從 plugin-sdk/test-fixtures 匯入 |
peekSystemEvents / resetSystemEventsForTest | 檢查並重設系統事件測試資料。從 plugin-sdk/test-fixtures 匯入 |
sanitizeTerminalText | 清理終端輸出以供斷言。從 plugin-sdk/test-fixtures 匯入 |
countLines / hasBalancedFences | 斷言分塊輸出的形狀。從 plugin-sdk/test-fixtures 匯入 |
typedCases | 保留表格驅動測試的常值型別。從 plugin-sdk/test-fixtures 匯入 |
src/plugins/contracts 下。
型別
聚焦測試的子路徑也會重新匯出測試檔案中實用的型別: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";
測試目標解析
使用installCommonResolveTargetErrorCases 為頻道目標解析新增標準錯誤案例:
import { describe } from "vitest";
import { installCommonResolveTargetErrorCases } from "openclaw/plugin-sdk/channel-target-testing";
describe("my-channel 目標解析", () => {
installCommonResolveTargetErrorCases({
resolveTarget: ({ to, mode, allowFrom }) => {
// 你的頻道目標解析邏輯
return myChannelResolveTarget({ to, mode, allowFrom });
},
implicitAllowFrom: ["user1", "user2"],
});
// 新增頻道特定的測試案例
it("應解析 @username 目標", () => {
// ...
});
});
測試模式
測試註冊合約
將手寫的api 模擬物件傳給 register(api) 的單元測試,不會
執行 OpenClaw 的載入器接受閘門。你的外掛所依賴的每個註冊介面都應至少新增一個由載入器支援的
冒煙測試,尤其是鉤子與記憶等專屬功能。
若缺少必要的中繼資料,或外掛呼叫不屬於自己的功能 API,真正的載入器會使外掛註冊失敗。例如,
api.registerHook(...) 需要鉤子名稱,而
api.registerMemoryCapability(...) 要求外掛資訊清單或匯出的
進入點宣告 kind: "memory"。
測試執行階段設定存取
優先使用openclaw/plugin-sdk/plugin-test-runtime 的共用外掛執行階段模擬物件。其執行階段設定輔助工具會模擬
目前的快照與變更 API。
對頻道外掛進行單元測試
import { describe, it, expect, vi } from "vitest";
describe("my-channel 外掛", () => {
it("應從設定解析帳號", () => {
const cfg = {
channels: {
"my-channel": {
token: "test-token",
allowFrom: ["user1"],
},
},
};
const account = myPlugin.setup.resolveAccount(cfg, undefined);
expect(account.token).toBe("test-token");
});
it("應檢查帳號而不具現化機密", () => {
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");
// 未公開權杖值
expect(inspection).not.toHaveProperty("token");
});
});
對供應商外掛進行單元測試
import { describe, it, expect } from "vitest";
describe("my-provider 外掛", () => {
it("應解析動態模型", () => {
const model = myProvider.resolveDynamicModel({
modelId: "custom-model-v2",
// ... 情境
});
expect(model.id).toBe("custom-model-v2");
expect(model.provider).toBe("my-provider");
expect(model.api).toBe("openai-completions");
});
it("當 API 金鑰可用時應傳回目錄", async () => {
const result = await myProvider.catalog.run({
resolveProviderApiKey: () => ({ apiKey: "test-key" }),
// ... 情境
});
expect(result?.provider?.models).toHaveLength(2);
});
});
模擬外掛執行階段
對於使用createPluginRuntimeStore 的程式碼,請在測試中模擬執行階段:
import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
import type { PluginRuntime } from "openclaw/plugin-sdk/runtime-store";
const store = createPluginRuntimeStore<PluginRuntime>({
pluginId: "test-plugin",
errorMessage: "測試執行階段尚未設定",
});
// 在測試設定中
const mockRuntime = {
agent: {
resolveAgentDir: vi.fn().mockReturnValue("/tmp/agent"),
// ... 其他模擬物件
},
config: {
current: vi.fn(() => ({}) as const),
mutateConfigFile: vi.fn(),
replaceConfigFile: vi.fn(),
},
// ... 其他命名空間
} as unknown as PluginRuntime;
store.setRuntime(mockRuntime);
// 測試後
store.clearRuntime();
使用個別執行個體的虛設常式進行測試
優先使用個別執行個體的虛設常式,而不是修改原型:// 建議:個別執行個體的虛設常式
const client = new MyChannelClient();
client.sendMessage = vi.fn().mockResolvedValue({ id: "msg-1" });
// 避免:修改原型
// MyChannelClient.prototype.sendMessage = vi.fn();
合約測試(存放於儲存庫內的外掛)
內建外掛具有驗證註冊所有權的合約測試:pnpm test src/plugins/contracts/
- 哪些外掛註冊哪些供應商
- 哪些外掛註冊哪些語音供應商
- 註冊形式的正確性
- 執行階段合約遵循情況
執行限定範圍的測試
針對特定外掛: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.mjs 會在 CI 中執行一組 lint:plugins:*
匯入邊界檢查;每項檢查也可以在本機獨立執行:
| 命令 | 強制規則 |
|---|---|
pnpm run lint:plugins:no-monolithic-plugin-sdk-entry-imports | 內建外掛不得匯入單體式的 openclaw/plugin-sdk 根彙總模組。 |
pnpm run lint:plugins:no-extension-src-imports | 正式環境的擴充功能檔案不得直接匯入儲存庫的 src/** 樹狀結構(../../src/...)。 |
pnpm run lint:plugins:no-extension-test-core-imports | 擴充功能測試檔案不得匯入已移除的 SDK 測試別名或其他僅限核心使用的測試輔助工具。 |
測試設定
OpenClaw 使用 Vitest 4,並提供資訊用途的 V8 覆蓋率報告。針對外掛測試:# 執行所有測試
pnpm test
# 執行特定外掛測試
pnpm test <bundled-plugin-root>/my-channel/src/channel.test.ts
# 使用特定測試名稱篩選條件執行
pnpm test <bundled-plugin-root>/my-channel/ -t "resolves account"
# 執行並產生覆蓋率報告
pnpm test:coverage
OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test