跳轉到主要內容

Documentation Index

Fetch the complete documentation index at: https://docs.openclaw.ai/llms.txt

Use this file to discover all available pages before exploring further.

OpenClaw Plugin 測試工具、模式與 lint 強制規則的參考。
想找測試範例嗎? 操作指南包含完整的測試範例: 頻道 Plugin 測試供應者 Plugin 測試

測試工具

Plugin API mock 匯入: openclaw/plugin-sdk/plugin-test-api 代理程式執行階段合約匯入: openclaw/plugin-sdk/agent-runtime-test-contracts 頻道合約匯入: openclaw/plugin-sdk/channel-contract-testing 頻道測試輔助工具匯入: openclaw/plugin-sdk/channel-test-helpers 頻道目標測試匯入: openclaw/plugin-sdk/channel-target-testing Plugin 合約匯入: openclaw/plugin-sdk/plugin-test-contracts Plugin 執行階段測試匯入: openclaw/plugin-sdk/plugin-test-runtime 供應者合約匯入: openclaw/plugin-sdk/provider-test-contracts 供應者 HTTP mock 匯入: openclaw/plugin-sdk/provider-http-test-mocks 環境/網路測試匯入: openclaw/plugin-sdk/test-env 通用測試資料匯入: openclaw/plugin-sdk/test-fixtures Node 內建 mock 匯入: openclaw/plugin-sdk/test-node-mocks 新的 Plugin 測試請優先使用下方聚焦的子路徑。廣泛的 openclaw/plugin-sdk/testing barrel 僅用於舊版相容性。 Repo guardrails 會拒絕新的實際匯入來源使用 plugin-sdk/testingplugin-sdk/test-utils;這些名稱僅保留作為外部 Plugin 和相容性記錄測試的已棄用相容介面。
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 {
  bundledPluginRoot,
  createCliRuntimeCapture,
  typedCases,
} from "openclaw/plugin-sdk/test-fixtures";
import { mockNodeBuiltinModule } from "openclaw/plugin-sdk/test-node-mocks";

可用的匯出項目

匯出項目用途
createTestPluginApi為直接註冊單元測試建置最小 Plugin API mock。從 plugin-sdk/plugin-test-api 匯入
AUTH_PROFILE_RUNTIME_CONTRACT原生 agent 執行階段適配器的共用驗證設定檔合約測試夾具。從 plugin-sdk/agent-runtime-test-contracts 匯入
DELIVERY_NO_REPLY_RUNTIME_CONTRACT原生 agent 執行階段適配器的共用傳遞抑制合約測試夾具。從 plugin-sdk/agent-runtime-test-contracts 匯入
OUTCOME_FALLBACK_RUNTIME_CONTRACT原生 agent 執行階段適配器的共用 fallback 分類合約測試夾具。從 plugin-sdk/agent-runtime-test-contracts 匯入
createParameterFreeTool為原生執行階段合約測試建置動態工具 schema 測試夾具。從 plugin-sdk/agent-runtime-test-contracts 匯入
expectChannelInboundContextContract斷言頻道傳入內容脈絡形狀。從 plugin-sdk/channel-contract-testing 匯入
installChannelOutboundPayloadContractSuite安裝頻道傳出 payload 合約案例。從 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格式化確定性的 envelope 時間戳記。從 plugin-sdk/channel-test-helpers 匯入
expectPairingReplyText斷言頻道配對回覆文字並擷取其程式碼。從 plugin-sdk/channel-test-helpers 匯入
describePluginRegistrationContract安裝 Plugin 註冊合約檢查。從 plugin-sdk/plugin-test-contracts 匯入
registerSingleProviderPlugin在 loader smoke 測試中註冊一個提供者 Plugin。從 plugin-sdk/plugin-test-runtime 匯入
registerProviderPlugin從一個 Plugin 擷取所有提供者種類。從 plugin-sdk/plugin-test-runtime 匯入
registerProviderPlugins擷取多個 Plugin 的提供者註冊。從 plugin-sdk/plugin-test-runtime 匯入
requireRegisteredProvider斷言提供者集合包含某個 ID。從 plugin-sdk/plugin-test-runtime 匯入
createRuntimeEnv建置模擬的 CLI/Plugin 執行階段環境。從 plugin-sdk/plugin-test-runtime 匯入
createPluginSetupWizardStatus為頻道 Plugin 建置設定狀態輔助工具。從 plugin-sdk/plugin-test-runtime 匯入
describeOpenAIProviderRuntimeContract安裝提供者系列執行階段合約檢查。從 plugin-sdk/provider-test-contracts 匯入
expectPassthroughReplayPolicy斷言提供者重播政策會透傳由提供者擁有的工具與中繼資料。從 plugin-sdk/provider-test-contracts 匯入
runRealtimeSttLiveTest使用共用音訊測試夾具執行即時 STT 提供者 live 測試。從 plugin-sdk/provider-test-contracts 匯入
normalizeTranscriptForMatch在模糊斷言前正規化 live 逐字稿輸出。從 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 mock。從 plugin-sdk/provider-http-test-mocks 匯入
installProviderHttpMockCleanup在每個測試後重設提供者 HTTP/驗證 mock。從 plugin-sdk/provider-http-test-mocks 匯入
installCommonResolveTargetErrorCases目標解析錯誤處理的共用測試案例。從 plugin-sdk/channel-target-testing 匯入
shouldAckReaction檢查頻道是否應加入 ack 反應。從 plugin-sdk/channel-feedback 匯入
removeAckReactionAfterReply在回覆傳遞後移除 ack 反應。從 plugin-sdk/channel-feedback 匯入
createTestRegistry建置頻道 Plugin registry 測試夾具。從 plugin-sdk/plugin-test-runtimeplugin-sdk/channel-test-helpers 匯入
createEmptyPluginRegistry建置空的 Plugin registry 測試夾具。從 plugin-sdk/plugin-test-runtimeplugin-sdk/channel-test-helpers 匯入
setActivePluginRegistry為 Plugin 執行階段測試安裝 registry 測試夾具。從 plugin-sdk/plugin-test-runtimeplugin-sdk/channel-test-helpers 匯入
createRequestCaptureJsonFetch在媒體輔助工具測試中擷取 JSON fetch 請求。從 plugin-sdk/test-env 匯入
withServer針對一次性本機 HTTP 伺服器執行測試。從 plugin-sdk/test-env 匯入
createMockIncomingRequest建置最小傳入 HTTP 請求物件。從 plugin-sdk/test-env 匯入
withFetchPreconnect在已安裝預連線 hook 的情況下執行 fetch 測試。從 plugin-sdk/test-env 匯入
withEnv / withEnvAsync暫時修補環境變數。從 plugin-sdk/test-env 匯入
createTempHomeEnv / withTempHome / withTempDir建立隔離的檔案系統測試夾具。從 plugin-sdk/test-env 匯入
createMockServerResponse建立最小 HTTP 伺服器回應 mock。從 plugin-sdk/test-env 匯入
createCliRuntimeCapture在測試中擷取 CLI 執行階段輸出。從 plugin-sdk/test-fixtures 匯入
importFreshModule使用新的查詢 token 匯入 ESM 模組以略過模組快取。從 plugin-sdk/test-fixtures 匯入
bundledPluginRoot / bundledPluginFile解析內建 Plugin 原始碼或 dist 測試夾具路徑。從 plugin-sdk/test-fixtures 匯入
mockNodeBuiltinModule安裝窄範圍的 Node 內建 Vitest mock。從 plugin-sdk/test-node-mocks 匯入
createSandboxTestContext建置 sandbox 測試內容脈絡。從 plugin-sdk/test-fixtures 匯入
writeSkill寫入 skill 測試夾具。從 plugin-sdk/test-fixtures 匯入
makeAgentAssistantMessage建置 agent 逐字稿訊息測試夾具。從 plugin-sdk/test-fixtures 匯入
peekSystemEvents / resetSystemEventsForTest檢查並重設系統事件測試夾具。從 plugin-sdk/test-fixtures 匯入
sanitizeTerminalText清理終端機輸出以用於斷言。從 plugin-sdk/test-fixtures 匯入
countLines / hasBalancedFences斷言分塊輸出形狀。從 plugin-sdk/test-fixtures 匯入
runProviderCatalog使用測試依賴項執行提供者目錄 hook
resolveProviderWizardOptions在合約測試中解析提供者設定精靈選項
resolveProviderModelPickerEntries在合約測試中解析提供者模型選擇器項目
buildProviderPluginMethodChoice建置提供者精靈選項 ID 以用於斷言
setProviderWizardProvidersResolverForTest為隔離測試注入提供者精靈提供者
createProviderUsageFetch建立提供者使用量擷取測試夾具
useFrozenTime / useRealTime凍結並還原計時器,用於時間敏感的測試。從 plugin-sdk/test-env 匯入
createTestWizardPrompter建立模擬的設定精靈提示器
createRuntimeTaskFlow建立隔離的執行階段任務流程狀態
typedCases保留表格驅動測試的字面值型別。從 plugin-sdk/test-fixtures 匯入
Bundled-plugin 合約套件也會使用 SDK 測試子路徑,提供僅限測試使用的登錄、manifest、公開成品與執行階段 fixture 輔助工具。依賴 bundled OpenClaw 清單的僅核心套件會保留在 src/plugins/contracts 底下。新的擴充功能測試應放在已記錄且聚焦的 SDK 子路徑上,例如 plugin-sdk/plugin-test-apiplugin-sdk/channel-contract-testingplugin-sdk/agent-runtime-test-contractsplugin-sdk/channel-test-helpersplugin-sdk/plugin-test-contractsplugin-sdk/plugin-test-runtimeplugin-sdk/provider-test-contractsplugin-sdk/provider-http-test-mocksplugin-sdk/test-envplugin-sdk/test-fixtures,而不是直接匯入範圍廣泛的 plugin-sdk/testing 相容性 barrel、repo src/** 檔案或 repo test/helpers/* 橋接。

型別

聚焦的測試子路徑也會重新匯出測試檔案中實用的型別:
import type {
  ChannelAccountSnapshot,
  ChannelGatewayContext,
} from "openclaw/plugin-sdk/channel-contract";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
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 target resolution", () => {
  installCommonResolveTargetErrorCases({
    resolveTarget: ({ to, mode, allowFrom }) => {
      // Your channel's target resolution logic
      return myChannelResolveTarget({ to, mode, allowFrom });
    },
    implicitAllowFrom: ["user1", "user2"],
  });

  // Add channel-specific test cases
  it("should resolve @username targets", () => {
    // ...
  });
});

測試模式

測試註冊合約

將手寫 api mock 傳給 register(api) 的單元測試,不會執行 OpenClaw 的載入器接受閘門。請為 Plugin 依賴的每個註冊介面至少加入一個由載入器支援的煙霧測試,特別是 hooks 以及像記憶體這類獨占能力。 當必要 metadata 缺失,或 Plugin 呼叫不屬於自己的能力 API 時,真正的載入器會讓 Plugin 註冊失敗。例如,api.registerHook(...) 需要 hook 名稱,而 api.registerMemoryCapability(...) 則需要 Plugin manifest 或匯出的進入點宣告 kind: "memory"

測試執行階段設定存取

測試 bundled 頻道 Plugin 時,優先使用來自 openclaw/plugin-sdk/channel-test-helpers 的共用 Plugin 執行階段 mock。其已棄用的 runtime.config.loadConfig()runtime.config.writeConfigFile(...) mock 預設會擲出錯誤,讓測試能捕捉相容性 API 的新用法。只有在測試明確涵蓋舊版相容性行為時,才覆寫這些 mock。

對頻道 Plugin 進行單元測試

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");
  });
});

對提供者 Plugin 進行單元測試

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 執行階段

對於使用 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: "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();

使用逐實例 stub 進行測試

優先使用逐實例 stub,而不是變更 prototype:
// Preferred: per-instance stub
const client = new MyChannelClient();
client.sendMessage = vi.fn().mockResolvedValue({ id: "msg-1" });

// Avoid: prototype mutation
// MyChannelClient.prototype.sendMessage = vi.fn();

合約測試(repo 內 Plugin)

Bundled Plugin 具有合約測試,用來驗證註冊所有權:
pnpm test -- src/plugins/contracts/
這些測試會斷言:
  • 哪些 Plugin 註冊哪些提供者
  • 哪些 Plugin 註冊哪些語音提供者
  • 註冊形狀正確性
  • 執行階段合約遵循情況

執行範圍限定測試

針對特定 Plugin:
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 強制規則(repo 內 Plugin)

pnpm check 會針對 repo 內 Plugin 強制執行三項規則:
  1. 禁止單體根匯入 — 拒絕使用 openclaw/plugin-sdk 根 barrel
  2. 禁止直接 src/ 匯入 — Plugin 不能直接匯入 ../../src/
  3. 禁止自我匯入 — Plugin 不能匯入自己的 plugin-sdk/<name> 子路徑
外部 Plugin 不受這些 lint 規則約束,但建議遵循相同模式。

測試設定

OpenClaw 使用 Vitest 搭配 V8 覆蓋率門檻。針對 Plugin 測試:
# 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
如果本機執行造成記憶體壓力:
OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test

相關