惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
J
Java Code Geeks
IT之家
IT之家
Last Week in AI
Last Week in AI
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
B
Blog RSS Feed
P
Proofpoint News Feed
博客园 - Franky
MongoDB | Blog
MongoDB | Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 【当耐特】
Microsoft Security Blog
Microsoft Security Blog
I
InfoQ
B
Blog
Stack Overflow Blog
Stack Overflow Blog
Martin Fowler
Martin Fowler
博客园 - 聂微东
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Apple Machine Learning Research
Apple Machine Learning Research
大猫的无限游戏
大猫的无限游戏
U
Unit 42
aimingoo的专栏
aimingoo的专栏

Chat SDK Documentation

TanStack AI | Chat SDK History | Chat SDK History | Chat SDK List a vendor-official adapter | Chat SDK Approvals | Chat SDK Vercel Connect | Chat SDK Teams Low-Level APIs | Chat SDK CLI | Chat SDK Platform Adapters | Chat SDK State Adapters | Chat SDK Cards | Chat SDK Getting Started | Chat SDK Introduction | Chat SDK Modals | Chat SDK Slack Low-Level APIs | Chat SDK Streaming | Chat SDK Overview | Chat SDK toAiMessages | Chat SDK Cards | Chat SDK Overview | Chat SDK Markdown | Chat SDK Modals | Chat SDK AI SDK Tools | Chat SDK Types | Chat SDK Message Subject | Chat SDK Conversation History | Chat SDK Transcripts | Chat SDK Slack bot with Next.js and Redis Actions | Chat SDK Direct Messages | Chat SDK
Testing | Chat SDK
Bot authors: test your handlers · 2026-05-29 · via Chat SDK Documentation

Test your bot handlers and custom adapters with @chat-adapter/tests — Vitest factories, custom matchers, and a setup file.

The @chat-adapter/tests package gives you Vitest factories, custom matchers, and a setup file for testing bots and custom adapters built on Chat SDK.

pnpm add -D @chat-adapter/tests

chat and vitest are peer dependencies — they should already be in your project.

Auto-register all matchers by adding the package's setup file to your Vitest config:

import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    setupFiles: ["@chat-adapter/tests/setup"],
  },
});

Without the setup file, register matchers manually:

import { matchers } from "@chat-adapter/tests/matchers";
expect.extend(matchers);
import {
  createMockAdapter,
  createMockChatInstance,
  createMockState,
  createTestMessage,
  mockLogger,
} from "@chat-adapter/tests";
FactoryReturnsNotes
createMockAdapter(name?, overrides?)AdapterEvery method is vi.fn() with sensible defaults
createMockChatInstance(options?)ChatInstanceEvery process* handler is vi.fn(); getState/getUserName/getLogger wired up
createMockState()MockStateAdapterIn-memory Maps for subscriptions, locks, KV, lists, queues; cache exposes the underlying map
createTestMessage(id, text, overrides?)MessageMarkdown text is parsed into the formatted AST
mockLogger / createMockLogger()LoggerShared default vs fresh-per-call
MatcherAsserts
expect(adapter).toHavePosted(threadId, textPattern?)adapter.postMessage was called for this thread
expect(adapter).toHaveEdited(threadId, messageId, textPattern?)adapter.editMessage was called for this message
expect(adapter).toHaveDeleted(threadId, messageId)adapter.deleteMessage was called for this message
expect(adapter).toHaveReactedWith(threadId, messageId, emoji)adapter.addReaction was called with the emoji (string or EmojiValue.name)
expect(adapter).toHaveStartedTyping(threadId)adapter.startTyping was called for this thread
expect(adapter).toHavePostedToChannel(channelId, textPattern?)adapter.postChannelMessage was called for this channel
expect(chat).toHaveDispatched(handler)The named process* handler on the mock ChatInstance was called
expect(state).toBeSubscribedTo(threadId)state.isSubscribed(threadId) resolves to true (async — await expect(...))

Text-pattern matchers extract a comparable string from AdapterPostableMessage — strings directly, PostableMarkdown.markdown, PostableRaw.raw, and PostableCard.fallbackText. AST-shaped messages and cards without fallbackText aren't text-matchable; assert without textPattern and inspect mock.calls directly.

When you're building a bot on top of Chat SDK, the kit lets you exercise your handlers without a real Slack/Teams/etc. webhook on the wire:

import { describe, expect, it } from "vitest";
import { Chat } from "chat";
import { createMockAdapter, createMockState } from "@chat-adapter/tests";

describe("bot handlers", () => {
  it("replies with a greeting on mention", async () => {
    const slack = createMockAdapter("slack");
    const state = createMockState();
    const bot = new Chat({
      userName: "mybot",
      adapters: { slack },
      state,
    });

    bot.onNewMention(async (thread) => {
      await thread.post("hello there");
    });

    // Drive a synthesized mention through the bot…
    // (use your adapter's webhook path or a thread-level call)

    expect(slack).toHavePosted("slack:C1:t1", /hello there/);
  });
});

When you're building a custom Adapter, the kit gives you a ChatInstance mock you can hand to your adapter and assert that webhooks route through the right process* hook with the right normalized payload:

import { describe, expect, it } from "vitest";
import { createMockChatInstance } from "@chat-adapter/tests";
import { MyAdapter } from "./adapter";

describe("MyAdapter.handleWebhook", () => {
  it("dispatches incoming messages through processMessage", async () => {
    const chat = createMockChatInstance();
    const adapter = new MyAdapter({ /* config */ });
    await adapter.initialize(chat);

    const request = new Request("https://example.com/webhook", {
      method: "POST",
      body: JSON.stringify({ /* platform-specific payload */ }),
      headers: { "content-type": "application/json" },
    });
    const response = await adapter.handleWebhook(request);

    expect(response.status).toBe(200);
    expect(chat).toHaveDispatched("processMessage");
  });
});

Helpers that depend on a specific platform's wire format (signed Slack webhooks, Teams claim builders, etc.) live in each adapter's own /testing subpath rather than in this kit, so adopting @chat-adapter/tests doesn't pull in adapter dependencies you don't use.

If you're contributing adapters or core to this repo, see the Testing adapters contributing guide for hand-rolled patterns used inside packages/.