Skip to content

Integration test #209

@josephktcheung

Description

@josephktcheung

Hi,

Related to #83, I'd like to share how I write Stealth's integration test. Source code can be found here https://github.com/josephktcheung/stealth-integration-test. @luizcarvalho @mgomes please take a look and see if this can be improved.

Steps:

  1. Run stealth new to generate a new stealth app

  2. Install following gems for testing

group :test do
  gem "rack-test"
  gem "rspec"
  gem "mock_redis"
end
  1. In spec/spec_helper.rb
# coding: utf-8
# frozen_string_literal: true


require 'rspec'
require 'stealth'
require 'mock_redis'
require 'sidekiq/testing'

# Requires supporting files with custom matchers and macros, etc,
# in ./support/ and its subdirectories.
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'bot'))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'config'))
$LOAD_PATH.unshift(File.dirname(__FILE__))

Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
require_relative "../bot/helpers/bot_helper"

ENV['STEALTH_ENV'] = 'test'

RSpec.configure do |config|
  I18n.load_path += Dir[File.join(File.dirname(__FILE__), '..', 'config', 'locales', '*.{rb,yml}')]
  config.include BotHelper
  config.before(:each) do |example|
    Sidekiq::Worker.clear_all
    Sidekiq::Testing.fake!
    $redis = MockRedis.new
    allow(Redis).to receive(:new).and_return($redis)
  end
  config.filter_run_when_matching :focus
  config.formatter = :documentation

  config.before(:suite) do
    Stealth.boot
  end
end
  1. Define sample_message class in spec/support/sample_message.rb
class SampleMessage

  def initialize(service:)
    @service = service
    @base_message = Stealth::ServiceMessage.new(service: @service)
    @base_message.sender_id = sender_id
    @base_message.timestamp = timestamp
    @base_message
  end

  def message_with_text(message)
    @base_message.message = message
    self
  end

  def message_with_payload(payload)
    @base_message.payload = payload
    self
  end

  def message_with_location(location)
    @base_message.location = location
    self
  end

  def message_with_attachments(attachments)
    @base_message.attachments = attachments
    self
  end

  def sender_id
    "8b3e0a3c-62f1-401e-8b0f-615c9d256b1f"
  end

  def timestamp
    @base_message.timestamp || Time.now
  end

  def to_request_json
    if @base_message.message.present?
      JSON.generate({
        entry: [
          {
            "messaging": [
              "sender": {
                "id": @base_message.sender_id
              },
              "recipient": {
                "id": "<PAGE_ID>"
              },
              "timestamp": @base_message.timestamp.to_i * 1000,
              "message": {
                "mid":"mid.1457764197618:41d102a3e1ae206a38",
                "text": @base_message.message
              }
            ]
          }
        ]
      })
    end
  end
end
  1. Define custom matcher send_reply in spec/support/matchers/send_reply.rb (Thanks @sunny for correction)
RSpec::Matchers.define :receive_message do |message|
  match do |client|
    stub = double("client")
    allow(stub).to receive(:transmit).and_return(true)
    @replies.each do |reply|
      expect(client).to receive(:new)
        .with(hash_including(reply: hash_including(reply)))
        .ordered
        .and_return(stub)
    end

    json = message.to_request_json
    post "/incoming/#{@service}", json, { "CONTENT_TYPE" => "application/json" }
  end

  chain :as_service do |service|
    @service = service
  end
  
  chain :and_send_replies do |replies|
    @replies = replies
  end
end
  1. In spec/features/chatbot_flow_spec.rb
require "spec_helper"

describe "chatbot flow" do
  include Rack::Test::Methods

  def app
    Stealth::Server
  end

  let(:message) { 
    SampleMessage.new(
      service: "facebook"
    )
  }

  let(:client) { Stealth::Services::Facebook::Client }

  it "handles user conversation" do
    Sidekiq::Testing.inline! do
      expect(client).to receive_message(
        message.message_with_text("hello")
      )
        .as_service("facebook")
        .and_send_replies([
          {
            "recipient" => {
              "id" => message.sender_id
            },
            "message" => {
              "text" => "Hello World!"
            }
          },
          {
            "recipient" => {
              "id" => message.sender_id
            },
            "message" => {
              "text" => "Goodbye World!"
            }
          }
        ])
    end
  end
end
  1. In bot/controllers/hellos_controller.rb
class HellosController < BotController

  def say_hello
    send_replies
    step_to flow: "goodbye"
  end

end
  1. Run bundle e rspec and test passes

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions