Skip to content

Testing

infinite-monkeys edited this page Apr 17, 2014 · 65 revisions

Sidekiq provides a few options for testing your workers.

Setup

Sidekiq provides the following three testing modes:

  • A test fake that pushes all jobs into a jobs array
  • An inline mode that runs the job immediately instead of enqueuing it
  • The test harness can be disabled. Jobs are pushed to redis.

Sidekiq allows you to dynamically configure the testing harness with the following methods:

require 'sidekiq/testing'
Sidekiq::Testing.fake! # fake is the default mode
Sidekiq::Testing.inline!
Sidekiq::Testing.disable!

Each of the above methods also accepts a block. An example:

require 'sidekiq/testing'
Sidekiq::Testing.fake!

# Some tests

Sidekiq::Testing.inline! do
  # Some other tests
end

# Here we're back to fake testing again.

To query the current state, use the following methods:

Sidekiq::Testing.enabled?
Sidekiq::Testing.disabled?
Sidekiq::Testing.fake?
Sidekiq::Testing.inline?

Testing Worker Queueing (Fake)

Similar to the ActionMailer testing API, instead of pushing jobs to Redis, Sidekiq pushes them into a jobs array which you can access. Require the sidekiq/testing file in your {test,spec}_helper.rb and set the mode:

require 'sidekiq/testing'
Sidekiq::Testing.fake!

Then assert that jobs were pushed on to the queue:

expect {
  HardWorker.perform_async(1, 2)
}.to change(HardWorker.jobs, :size).by(1)
assert_equal 0, HardWorker.jobs.size
HardWorker.perform_async(1, 2)
assert_equal 1, HardWorker.jobs.size

You can execute all queued jobs by draining the queue:

HardWorker.perform_async(1, 2)
HardWorker.perform_async(2, 3)
assert_equal 2, HardWorker.jobs.size
HardWorker.drain
assert_equal 0, HardWorker.jobs.size

If you would like to remove jobs from the queue without actually performing them:

HardWorker.perform_async(1, 2)
HardWorker.jobs.clear
assert_equal 0, HardWorker.jobs.size

To clear all workers' jobs:

Sidekiq::Worker.clear_all

This can be useful to make sure jobs don't linger between tests:

RSpec.configure do |config|
  config.before(:each) do
    Sidekiq::Worker.clear_all
  end
end

Testing Workers Directly

To test your worker directly, just treat it like a ruby object. Easy!

work = HardWorker.new
work.perform(1, 2)

Testing Workers Inline

You can run Sidekiq workers inline in your tests by requiring the sidekiq/testingfile in your {test,spec}_helper.rb and setting the mode:

require 'sidekiq/testing'
Sidekiq::Testing.inline!

Jobs will then be executed immediately when they are placed on the queue.

Testing delay and delay_for methods

You can access jobs by Sidekiq::Extensions::DelayedMailer (ActionMailer), Sidekiq::Extensions::DelayedModel (ActiveRecord), or Sidekiq::Extensions::DelayedClass (everything else)

assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
MyMailer.delay.send_welcome_email('foo@example.com')
assert_equal 1, Sidekiq::Extensions::DelayedMailer.jobs.size

RSpec

Example Hooks

This example looks for a :sidekiq tag in the example. If the :sidekiq tag isn't present and the example is tagged as an acceptance test, it defaults to inline testing. If the :sidekiq tag isn't present and the example is not an acceptance test, it defaults to fake testing.

require 'sidekiq/testing'

RSpec.configure do |config|
  config.before(:each) do |example_method|
    # Clears out the jobs for tests using the fake testing
    Sidekiq::Worker.clear_all
    # Get the current example from the example_method object
    example = example_method.example

    if example.metadata[:sidekiq] == :fake
      Sidekiq::Testing.fake!
    elsif example.metadata[:sidekiq] == :inline
      Sidekiq::Testing.inline!
    elsif example.metadata[:type] == :acceptance
      Sidekiq::Testing.inline!
    else
      Sidekiq::Testing.fake!
    end
  end
end

Usage:

describe SomeClass, sidekiq: :fake do
  # tests
end

describe SomeOtherClass, sidekiq: :inline do
  # tests
end

Custom Matchers

Here are some RSpec matchers you can use to streamline rspec tests:

MyWorker.should have_queued_job(2)
Collector.should have_queued_job_at(Time.new(2012,1,23,14,00),2)

There is also rspec-sidekiq which contains a collection of matchers for testing job queues and attributes

expect(AwesomeJob).to be_processed_in :download
expect(AwesomeJob).to be_retryable true
expect(AwesomeJob).to be_retryable 5
expect(AwesomeJob).to be_unique
expect(AwesomeJob).to have_enqueued_job("Awesome", true)
expect(Object.method :is_a?).to be_delayed(Object).until 1.hour.from_now

Clone this wiki locally