💎 ChatSDK Ruby

Testing

Test your bot logic with FakeAdapter and simulation methods without calling external APIs.

ChatSDK includes a complete testing toolkit so you can verify your bot logic without calling any external APIs.

FakeAdapter

ChatSDK::Testing::FakeAdapter is an in-memory adapter that records every outbound call. It also provides simulation methods to dispatch inbound events.

require "chat_sdk"
require "chat_sdk/testing"

bot = ChatSDK::Testing.build_bot

# build_bot creates a Chat with:
#   adapters: { test: FakeAdapter.new }
#   state: State::Memory.new
#   user_name: "test-bot"

Simulating Events

The FakeAdapter provides simulation methods that dispatch events through the full handler pipeline.

simulate_mention

adapter = bot.config.adapters[:test]

bot.on_new_mention do |thread, message|
  thread.post("Echo: #{message.text}")
end

adapter.simulate_mention(bot, text: "hello", user_id: "U123", channel_id: "C123")

assert_equal 1, adapter.posted_messages.size
assert_equal "Echo: hello", adapter.posted_messages.first.message.text

simulate_action

bot.on_action("approve_btn") do |event|
  event.thread.post("Approved by #{event.user.name}!")
end

adapter.simulate_action(bot, action_id: "approve_btn", value: "item-1")

assert_equal 1, adapter.posted_messages.size

simulate_reaction

bot.on_reaction do |event|
  event.thread.post("Got a :#{event.emoji}:!")
end

adapter.simulate_reaction(bot, emoji: "thumbsup")

simulate_slash_command

bot.on_slash_command("/deploy") do |event|
  event.thread.post("Deploying #{event.text}")
end

adapter.simulate_slash_command(bot, command: "/deploy", text: "production")

Inspecting Outbound Calls

FakeAdapter records calls in arrays you can assert against:

AttributeRecords
posted_messagespost_message calls
edited_messagesedit_message calls
deleted_messagesdelete_message calls
ephemeral_messages_sentpost_ephemeral calls
reactions_addedadd_reaction calls
reactions_removedremove_reaction calls
files_uploadedupload_file calls
modals_openedopen_modal calls
typing_startedstart_typing calls
dm_channelsopen_dm calls

Each recorded call is a RecordedCall struct with a type and keyword attributes matching the method parameters.

Resetting State

Call adapter.reset! between tests to clear all recorded calls:

# RSpec
before(:each) do
  adapter.reset!
end

Testing Helpers

Include ChatSDK::Testing::Helpers in your test suite for convenience methods:

RSpec.configure do |config|
  config.include ChatSDK::Testing::Helpers
end

RSpec.describe "MyBot" do
  it "responds to mentions" do
    adapter = ChatSDK::Testing::FakeAdapter.new
    bot = build_bot(adapters: { test: adapter })

    bot.on_new_mention do |thread, message|
      thread.post("Hi!")
    end

    adapter.simulate_mention(bot, text: "hello")
    expect(adapter.posted_messages.size).to eq(1)
  end
end

Available helpers:

  • build_bot(**options) -- Creates a Chat with FakeAdapter and Memory state
  • fake_adapter -- Returns a memoized FakeAdapter instance
  • build_message(text:, user_id:, ...) -- Creates a Message object for unit tests
  • build_card(title:, &block) -- Creates a card node via ChatSDK.card

Shared Contract Examples

ChatSDK provides RSpec shared examples for verifying custom adapters and state backends:

Adapter Contract

require "chat_sdk/testing/adapter_contract"

RSpec.describe MyCustomAdapter do
  subject { MyCustomAdapter.new(...) }
  it_behaves_like "a chat_sdk platform adapter"
end

This verifies that your adapter responds to all required methods and raises NotSupportedError for undeclared capabilities.

State Contract

require "chat_sdk/testing/state_contract"

RSpec.describe MyCustomState do
  subject { MyCustomState.new(...) }
  it_behaves_like "a chat_sdk state adapter"
end

This verifies subscriptions, locks (acquire, release, force), and key-value operations (get, set, delete, set_if_absent).

On this page