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

推荐订阅源

J
Java Code Geeks
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
爱范儿
爱范儿
罗磊的独立博客
美团技术团队
Jina AI
Jina AI
量子位
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
有赞技术团队
有赞技术团队
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
小众软件
小众软件
IT之家
IT之家
雷峰网
雷峰网
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 司徒正美
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
月光博客
月光博客
人人都是产品经理
人人都是产品经理
博客园 - 三生石上(FineUI控件)

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/.