Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing RSpec for Sneakers and Rabbitmq

I have been recently developing Message Queue using Rabbitmq and Sneakers Workers. I had a look on this guide https://github.com/jondot/sneakers/wiki/Testing-Your-Worker

But I still have no idea about how to develop the tests for them. I'm very grateful if there are any advices.

like image 861
Will Nguyen Avatar asked Jan 05 '19 04:01

Will Nguyen


1 Answers

Suggested guide contains Sneakers::Testing module to store all published messages. In order to do that you need to patch/stub method Sneakers::Publisher#publish to save published messages into Sneakers::Testing.messages_by_queue. And then use that data for expectations or somehow supply the data to worker classes.

Probably in most cases you just need inline execution for delayed jobs in specs. So can just patch Sneakers::Publisher#publish to get worker class by queue name and call its work method with received payload message. Initial setup:

# file: spec/support/sneakers_publish_inline_patch.rb

# Get list of all sneakers workers in app.
# You can just assign $sneakers_workers to your workers:
#
#     $sneakers_workers = [MyWorker1, MyWorker2, MyWorker3]
#
# Or get all classes where included Sneakers::Worker
# Note: can also load all classes with Rails.application.eager_load!
Dir[Rails.root.join('app/workers/**/*.rb')].each { |path| require path }
$sneakers_workers = ObjectSpace.each_object(Class).select { |c| c.included_modules.include? Sneakers::Worker }

# Patch method `publish` to find a worker class by queue and call its method `work`.
module Sneakers
  class Publisher
    def publish(msg, opts)
      queue = opts[:to_queue]
      worker_class = $sneakers_workers.find { |w| w.queue_name == queue }
      worker_class.new.work(msg)
    end
  end
end

Or stub method for one worker in a spec:

allow_any_instance_of(Sneakers::Publisher).to receive(:publish) do |_obj, msg, _opts|
  MyWorker.new.work(msg)
end

Or add a shared context for convenient way to enable inline jobs execution:

# file: spec/support/shared_contexts/sneakers_publish_inline.rb

shared_context 'sneakers_publish_inline' do
  let(:sneakers_worker_classes) do
    Dir[Rails.root.join('app/workers/**/*.rb')].each { |f| require f }
    ObjectSpace.each_object(Class).select { |c| c.included_modules.include? Sneakers::Worker }
  end

  before do
    allow_any_instance_of(Sneakers::Publisher).to receive(:publish) do |_obj, msg, opts|
      queue = opts[:to_queue]
      worker_class = sneakers_worker_classes.find { |w| w.queue_name == queue }
      raise ArgumentError, "Cannot find Sneakers worker class for queue: #{queue}" unless worker_class
      worker_class.new.work(msg)
    end
  end
end

Then just add include_context 'sneakers_publish_inline' in spec where you need inline jobs execution.

like image 168
Alex Avoiants Avatar answered Oct 28 '22 08:10

Alex Avoiants