Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 如何使用rspec测试电报机器人_Ruby_Rspec_Telegram Bot - Fatal编程技术网

Ruby 如何使用rspec测试电报机器人

Ruby 如何使用rspec测试电报机器人,ruby,rspec,telegram-bot,Ruby,Rspec,Telegram Bot,我正在构建一个简单的机器人,我需要测试以下方法,但无论我做什么,我都会遇到一个看起来像无限循环的东西 我想测试的方法 def main_method(token) @id_array = [] Telegram::Bot::Client.run(token) do |bot| bot.listen do |message| @id_array.push(message.chat.id) unless @id_array.include?(message.chat.id) ca

我正在构建一个简单的机器人,我需要测试以下方法,但无论我做什么,我都会遇到一个看起来像无限循环的东西

我想测试的方法

def main_method(token)
@id_array = []
Telegram::Bot::Client.run(token) do |bot|
  bot.listen do |message|
    @id_array.push(message.chat.id) unless @id_array.include?(message.chat.id)
    case message.text
    when '/quote'
      quote, author = RandomQuote.new.get_quote!
      bot.api.send_message(chat_id: message.chat.id, text: "#{quote}\n\t- Author: #{author}")
    when '/joke'
      bot.api.send_message(chat_id: message.chat.id, text: "What's your name?")
      bot.listen do |name|
        if name
          @joke = Jokes.new.random_joke(name.text)
          break
        end
      end
      bot.api.send_message(chat_id: message.chat.id, text: @joke.to_s)
    when '/help'
      bot.api.send_message(chat_id: message.chat.id, text: 'Type /joke to receive a custom joke with your name')
      bot.api.send_message(chat_id: message.chat.id, text: 'Type /quote to receive an inspirational quote')
    end
  end
end
结束

我的机器人正在工作并完成它的任务,我只需要任何测试用例来证明它的功能。

好吧,它不是一种“无限循环”,它是事件侦听器(
bot.listen
),它是一种轮询一些端口以获取一些消息,然后对此事件触发react

因此,您需要使用OOP逻辑将此方法拆分为小块,因为对于一个特定的方法有大量的案例,然后使用mock用法将每个块作为一个专用类进行测试

要处理初始化,您可以使用VCR磁带将真实请求存根到API中,并将令牌替换为测试令牌

例如,我们可以模拟main_方法以确保调用:listener,如下所示:

class MyRubyBot
  def main_method(token)
    ...
  end
end


around do |example|
  VCR.use_cassette('telegram_bot') do
    example.run
  end
end

it 'fires run on telegram bot' do
  expect(telegram_bot).to receive(:run).with("YOU_TEST_TOKEN")
  MyRubyBot.new.main_method("YOU_TEST_TOKEN")
end

it 'fires listener' do
  expect_any_instance_of(Telegram::Bot::Client).to receive(:listen)
  MyRubyBot.new.main_method("YOU_TEST_TOKEN")
end
def main_method(token)
  @id_array = []
  Telegram::Bot::Client.run(token) do |bot|
    bot.listen do |message|
      @id_array.push(message.chat.id) unless @id_array.include?(message.chat.id)
      MessageHandler.new(bot: bot, message: message).handle_message
    end
  end
end

class MessageHandler
  attr_accessor :bot, :message

  def initialize(bot:, message:)
    self.bot = bot
    self.message = message
  end

  def handle_message
    case message.text
    when '/quote'
      QuoteMessage.new(bot: bot, message: message).send_response
    when '/joke'
      joke_message = JokeMessage.new(bot: bot, message: message)
      joke_message.send_response
      @joke = joke_message.joke
    when '/help'
      HelpMessage.new(bot: bot, message: message).send_response
    end
  end
end
然后您需要在时将每个
的逻辑放入专用类,例如:

class QuoteMessage
  attr_accessor :bot, :message

  def initialize(bot:, message:)
    self.bot = bot
    self.message = message
  end

  def send_response
    quote, author = RandomQuote.new.get_quote!
    bot.api.send_message(chat_id: message.chat.id, text: "#{quote}\n\t- Author: #{author}")
  end
end
class QuoteMessage
  attr_accessor :bot, :message, :joke

  def initialize(bot:, message:)
    self.bot = bot
    self.message = message
  end

  def send_response
    bot.api.send_message(chat_id: message.chat.id, text: "What's your name?")
    bot.listen do |name|
      if name
        @joke = Jokes.new.random_joke(name.text)
        break
      end
    end
    bot.api.send_message(chat_id: message.chat.id, text: @joke.to_s)
  end
end
class HelpMessage
  attr_accessor :bot, :message

  def initialize(bot:, message:)
    self.bot = bot
    self.message = message
  end

  def send_response
    bot.api.send_message(chat_id: message.chat.id, text: 'Type /joke to receive a custom joke with your name')
    bot.api.send_message(chat_id: message.chat.id, text: 'Type /quote to receive an inspirational quote')
  end
end

describe MessageHandler do
  subject { described_class.new(bot: bot, message: message) }
  let(:bot) { double }
  let(:api) { double }

  context 'when quote' do
    let(:message) { '/quote' }
    let(:quote_message) { instance_double QuoteMessage }

    it 'fires QuoteMessage instance' do
      expect(QuoteMessage).to receive(:new).with(bot: bot, message: message).and_return quote_message 
      expect(quote_message).to receive(:send_response) 
      subject.handle_message
    end
  end
end
很好,现在我们可以将每个类作为任何东西分别测试,例如:

describe QuoteMessage do
  let(:bot) { double }
  let(:api) { double }
  let(:message) { double }

  it 'fires send_message' do
    expect(bot).to receive(:api).and_return(api)
    expect(api).to receive(:send_message).with(...)
    described_class.new(bot: bot, message: message).send_response
  end
end
# ... same logic for other classes

快到了!现在,我们可以使用新的MessageHandler类稍微重构主方法,如下所示:

class MyRubyBot
  def main_method(token)
    ...
  end
end


around do |example|
  VCR.use_cassette('telegram_bot') do
    example.run
  end
end

it 'fires run on telegram bot' do
  expect(telegram_bot).to receive(:run).with("YOU_TEST_TOKEN")
  MyRubyBot.new.main_method("YOU_TEST_TOKEN")
end

it 'fires listener' do
  expect_any_instance_of(Telegram::Bot::Client).to receive(:listen)
  MyRubyBot.new.main_method("YOU_TEST_TOKEN")
end
def main_method(token)
  @id_array = []
  Telegram::Bot::Client.run(token) do |bot|
    bot.listen do |message|
      @id_array.push(message.chat.id) unless @id_array.include?(message.chat.id)
      MessageHandler.new(bot: bot, message: message).handle_message
    end
  end
end

class MessageHandler
  attr_accessor :bot, :message

  def initialize(bot:, message:)
    self.bot = bot
    self.message = message
  end

  def handle_message
    case message.text
    when '/quote'
      QuoteMessage.new(bot: bot, message: message).send_response
    when '/joke'
      joke_message = JokeMessage.new(bot: bot, message: message)
      joke_message.send_response
      @joke = joke_message.joke
    when '/help'
      HelpMessage.new(bot: bot, message: message).send_response
    end
  end
end
因此,现在我们可以通过
和_yield
方法测试
MessageHandler
的火灾:


it 'fires MessageHandler' do
  message = 'any_message'
  expect(bot).to receive(:listen).and_yield(MessageHandler.new(bot: bot, message: message))
  MyRubyBot.new.main_method("YOU_TEST_TOKEN")
end
至少,我们需要测试MessageHandler,现在应该很容易,例如:

class QuoteMessage
  attr_accessor :bot, :message

  def initialize(bot:, message:)
    self.bot = bot
    self.message = message
  end

  def send_response
    quote, author = RandomQuote.new.get_quote!
    bot.api.send_message(chat_id: message.chat.id, text: "#{quote}\n\t- Author: #{author}")
  end
end
class QuoteMessage
  attr_accessor :bot, :message, :joke

  def initialize(bot:, message:)
    self.bot = bot
    self.message = message
  end

  def send_response
    bot.api.send_message(chat_id: message.chat.id, text: "What's your name?")
    bot.listen do |name|
      if name
        @joke = Jokes.new.random_joke(name.text)
        break
      end
    end
    bot.api.send_message(chat_id: message.chat.id, text: @joke.to_s)
  end
end
class HelpMessage
  attr_accessor :bot, :message

  def initialize(bot:, message:)
    self.bot = bot
    self.message = message
  end

  def send_response
    bot.api.send_message(chat_id: message.chat.id, text: 'Type /joke to receive a custom joke with your name')
    bot.api.send_message(chat_id: message.chat.id, text: 'Type /quote to receive an inspirational quote')
  end
end

describe MessageHandler do
  subject { described_class.new(bot: bot, message: message) }
  let(:bot) { double }
  let(:api) { double }

  context 'when quote' do
    let(:message) { '/quote' }
    let(:quote_message) { instance_double QuoteMessage }

    it 'fires QuoteMessage instance' do
      expect(QuoteMessage).to receive(:new).with(bot: bot, message: message).and_return quote_message 
      expect(quote_message).to receive(:send_response) 
      subject.handle_message
    end
  end
end
粗暴地说,我在某些细节上可能是错的,但总的来说,我想把方法

JFYI:试着问一些更具体的问题,因为看起来你在这里有一个任务,你决定把它放在同样的形式,所以,这不是最好的方式,这就是为什么很多人忽视它的原因。我希望有帮助,保重