Passer au contenu principal

Tests du Plugin

Référence pour les utilitaires de test, les modèles et l’application des règles de lint pour les plugins OpenClaw.
Vous cherchez des exemples de test ? Les guides pratiques incluent des exemples de test complets : Tests de plugins de canal et Tests de plugins de fournisseur.

Utilitaires de test

Importation : openclaw/plugin-sdk/testing Le sous-chemin de test exporte un ensemble restreint d’assistants pour les auteurs de plugins :
import {
  installCommonResolveTargetErrorCases,
  shouldAckReaction,
  removeAckReactionAfterReply,
} from "openclaw/plugin-sdk/testing";

Exports disponibles

ExportBut
installCommonResolveTargetErrorCasesCas de test partagés pour la gestion des erreurs de résolution de cible
shouldAckReactionVérifier si un canal doit ajouter une réaction d’accusé de réception
removeAckReactionAfterReplySupprimer la réaction d’accusé de réception après l’envoi de la réponse

Types

Le sous-chemin de test réexporte aussi des types utiles dans les fichiers de test :
import type {
  ChannelAccountSnapshot,
  ChannelGatewayContext,
  OpenClawConfig,
  PluginRuntime,
  RuntimeEnv,
  MockFn,
} from "openclaw/plugin-sdk/testing";

Tester la résolution de cible

Utilisez installCommonResolveTargetErrorCases pour ajouter des cas d’erreur standard pour la résolution de cible de canal :
import { describe } from "vitest";
import { installCommonResolveTargetErrorCases } from "openclaw/plugin-sdk/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", () => {
    // ...
  });
});

Modèles de test

Test unitaire d’un Plugin de canal

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

Test unitaire d’un Plugin de fournisseur

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

Simuler le runtime du Plugin

Pour le code qui utilise createPluginRuntimeStore, simulez le runtime dans les tests :
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: {
    loadConfig: vi.fn(),
    writeConfigFile: vi.fn(),
  },
  // ... other namespaces
} as unknown as PluginRuntime;

store.setRuntime(mockRuntime);

// After tests
store.clearRuntime();

Tester avec des stubs par instance

Préférez les stubs par instance à la mutation du 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();

Tests de contrat (plugins du dépôt)

Les plugins intégrés disposent de tests de contrat qui vérifient la propriété de l’enregistrement :
pnpm test -- src/plugins/contracts/
Ces tests vérifient :
  • Quels plugins enregistrent quels fournisseurs
  • Quels plugins enregistrent quels fournisseurs vocaux
  • La justesse de la forme d’enregistrement
  • La conformité au contrat d’exécution

Exécuter des tests ciblés

Pour un plugin spécifique :
pnpm test -- <bundled-plugin-root>/my-channel/
Pour les tests de contrat uniquement :
pnpm test -- src/plugins/contracts/shape.contract.test.ts
pnpm test -- src/plugins/contracts/auth.contract.test.ts
pnpm test -- src/plugins/contracts/runtime.contract.test.ts

Application des règles de lint (plugins du dépôt)

Trois règles sont appliquées par pnpm check pour les plugins du dépôt :
  1. Pas d’imports racine monolithiques — le barrel racine openclaw/plugin-sdk est rejeté
  2. Pas d’imports directs depuis src/ — les plugins ne peuvent pas importer ../../src/ directement
  3. Pas d’auto-imports — les plugins ne peuvent pas importer leur propre sous-chemin plugin-sdk/<name>
Les plugins externes ne sont pas soumis à ces règles de lint, mais il est recommandé de suivre les mêmes modèles.

Configuration des tests

OpenClaw utilise Vitest avec des seuils de couverture V8. Pour les tests de plugins :
# 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
Si les exécutions locales provoquent une pression mémoire :
OPENCLAW_VITEST_MAX_WORKERS=1 pnpm test

Voir aussi