slack-ruby / slack-ruby-bot

The easiest way to write a Slack bot in Ruby.
MIT License
1.12k stars 187 forks source link

Testing for two outputs from a command #272

Closed CeeBeeUK closed 3 years ago

CeeBeeUK commented 3 years ago

Say I have a command that is taking ~5 seconds to compile (not long enough to need an async call, but too long to not update the user!) Something like

module MyBot
  module Commands
    class AddUser < SlackRubyBot::Commands::Base
      command(/add (user|users)/) do |client, data, match|
        client.typing(channel: data.channel)
        create_user_script
        client.say(channel: data.channel, text: 'User created')
      end
    end
  end
end

I can test for the typing command with

it 'returns a valid response' do
  expect(message: user_input, channel: 'channel').to start_typing(channel: 'channel')
end

But if I want to test for the final message too I am running into issues...

it 'returns a valid response' do
  expect(message: user_input, channel: 'channel').to start_typing(channel: 'channel')
  expect(message: user_input, channel: 'channel').to respond_with_slack_message('User created')
end

Fails because, I'm guessing, a single message is being sent - a second message isn't magically triggering the second step?

Is there a work-around for this scenario?

It feels almost like I want a .then extension of Rspec!?

it 'returns a valid response' do
  expect(message: user_input, channel: 'channel').to start_typing(channel: 'channel').then.respond_with_slack_message('User created')
end
dblock commented 3 years ago

I think this is what you're looking for?

CeeBeeUK commented 3 years ago

I ended up changing the pattern before investigating... closing for now but will investigate and add example if I get it working! 😄

CeeBeeUK commented 3 years ago

I've finally found the time to come back to this and am having some issues and would really appreciate a steer. I have a command

module MySlackBot
  module Commands
    class Test < SlackRubyBot::Commands::Base
      command 'test' do |client, data, match|
        client.typing(channel: data.channel)
        sleep(3)
        client.say(channel: data.channel, text: 'Oh! Hi!!')
      end
    end
  end
end

Adding the test you suggested...

require 'spec_helper'

describe MySlackBot::Commands::Test do
  let(:app) { SlackRubyBot::Server.new }
  let(:client) { app.send(:client) }
  let(:message_hook) { SlackRubyBot::Hooks::Message.new }
  it 'receives typing' do
    expect(client).to receive(:typing)
    message_hook.call(client, Hashie::Mash.new(text: "#{SlackRubyBot.config.user} test", channel: 'channel'))
  end
end

(Note: I added I added the SlackRubyBot:: to Server.new as otherwise I had a uninitialized constant Server NameError) Results in

Slack::RealTime::Client::ClientNotStartedError:
       Slack::RealTime::Client::ClientNotStartedError

How do I go about stubbing out the server successfully?

If you can provide some pointers I would gladly open a PR to update the README :)

dblock commented 3 years ago

I think you don't need that server/app at all anymore if you assign a client a team, then the whole server trying to start won't matter. I am looking at https://github.com/slack-ruby/slack-shellbot/blob/3afcaa82c0030d90984ef83d3795bdb82a193e23/spec/commands/cd_spec.rb#L4 that does this:

let(:team) { Fabricate(:team) } # I think a stub or a Team.new will work here too
let(:client) { SlackShellbot::Web::Client.new(token: 'token', team: team) }
let(:message_hook) { SlackShellbot::Commands::Base }
CeeBeeUK commented 3 years ago

For FutureCeeBee/anyone who searches for this keyword... I now have tests passing on a pure slack-ruby-bot client using

require 'spec_helper'

describe MyBot::Commands::LongTest do
  let!(:client) { SlackRubyBot::App.new.send(:client) }
  let(:message_hook) { SlackRubyBot::Hooks::Message.new }
  let(:params) do
    Hashie::Mash.new(
      text: "#{SlackRubyBot.config.user} run long test",
      channel: 'channel',
      user: 'user'
    )
  end

  it 'responds with a warning message' do
    expect(client).to receive(:typing)
    expect(client).to receive(:say).with({ channel: 'channel', text: 'User created' })
    message_hook.call(client, params)
  end
end

calling

module MyBot
  module Commands
    class LongTest < SlackRubyBot::Commands::Base
      def self.user_create_script(_match)
        sleep 5
      end

      command(/run long test/) do |client, data, match|
        client.typing(channel: data.channel)
        user_create_script(match)
        client.say(channel: data.channel, text: 'User created')
      end
    end
  end
end