Testing
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.
Install
chat and vitest are peer dependencies, so install them in your project if they aren't there already.
Setup file (recommended)
Register all matchers automatically by adding the package's setup file to your Vitest config:
Without the setup file, register matchers manually:
Mock factories
| Factory | Returns | Notes |
|---|---|---|
createMockAdapter(name?, overrides?) | Adapter | Every method is vi.fn() with sensible defaults |
createMockChatInstance(options?) | ChatInstance | Every process* handler is vi.fn(); getState/getUserName/getLogger wired up |
createMockState() | MockStateAdapter | In-memory Maps for subscriptions, locks, KV, lists, queues; cache exposes the underlying map |
createTestMessage(id, text, overrides?) | Message | Markdown text is parsed into the formatted AST |
mockLogger / createMockLogger() | Logger | Shared default vs fresh-per-call |
Matchers
| Matcher | Asserts |
|---|---|
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. This matcher is async, so use 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 can't be matched by text. For those, assert without textPattern and inspect mock.calls directly.
Bot authors: test your handlers
If you're building a bot on Chat SDK, you can exercise your handlers with a mock adapter and mock state instead of real platform webhooks:
Adapter authors: test webhook → dispatch
If you're building a custom Adapter, pass it a mock ChatInstance and assert that webhooks route through the right process* hook with the right normalized payload:
Adapter-specific helpers
Helpers that depend on one platform's wire format, such as signed Slack webhooks or Teams claim builders, live in each adapter's own /testing subpath rather than in this package. That way, 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 the hand-rolled patterns used inside packages/.
Read more
State Adapters
State adapters store thread subscriptions, distributed locks, and cached data for your bot.
Handling Events
Register handlers for mentions, messages, reactions, member joins, and platform-specific events.
Testing adapters
Write unit tests, integration tests, and replay tests for community Chat SDK adapters.
Getting Started
Pick a guide to start building with Chat SDK.