Testing

David Gil edited this page Aug 10, 2018 · 55 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

To execute all workers' queued jobs:

Sidekiq::Worker.drain_all

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

HardWorker.perform_async(1, 2)
HardWorker.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
module SidekiqMinitestSupport
  def after_teardown
    Sidekiq::Worker.clear_all
    super
  end
end

class MiniTest::Spec
  include SidekiqMinitestSupport
end

class MiniTest::Unit::TestCase
  include SidekiqMinitestSupport
end

Queue API (4.0+)

A testing API for queues is available in 4.0+. This is helpful when the Worker class does not exist in the application being tested. One might enqueue a job using Sidekiq::Client:

Sidekiq::Client.push(
  'class' => 'NonExistentWorker',
  'queue' => 'other',
  'args' => [1]
)

Since the NonExistentWorker doesn't exist in the application, we can assert the job made it to the queue:

assert_equal 0, Sidekiq::Queues["other"].size

Sidekiq::Client.push(
  'class' => 'NonExistentWorker',
  'queue' => 'other',
  'args' => [1]
)

assert_equal 1, Sidekiq::Queues["other"].size
assert_equal "NonExistentWorker", Sidekiq::Queues["other"].first["class"]

# Clear an individual queue
Sidekiq::Queues["other"].clear

# Clear all queues (equivalent to Sidekiq::Worker.clear_all)
Sidekiq::Queues.clear_all

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!

# or use block mode to avoid leaking the setting into other tests
Sidekiq::Testing.inline! do
  HardWorker.perform_async
  assert_worked_hard
end

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

Testing delay and delay_for methods

You can access the jobs queue from Sidekiq::Extensions::DelayedMailer (ActionMailer), Sidekiq::Extensions::DelayedModel (ActiveRecord), or Sidekiq::Extensions::DelayedClass (everything else) similarly.

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

Testing Server Middleware

By default, the test harness (in either mode) does not run any server side middleware. You can add middleware to the harness with the following:

Sidekiq::Testing.server_middleware do |chain|
  chain.add AwesomeMiddleware
end

Middleware in the Sidekiq::Testing.server_middleware stack will be run in inline mode whenever jobs are run and in fake mode whenever .drain or .perform_one are called.

API

Sidekiq's API does not have a testing mode, e.g. something like Sidekiq::ScheduledSet.new.each(...) will always hit Redis. You can use Sidekiq::Testing.disable! to set up jobs in order to use the API in your tests against a real Redis instance.

Testing Batches

Batches are a high-level integration between many jobs. It's difficult to actually run the full batch implementation when testing: I suggest firing callbacks manually rather than trying to set up the batch middleware.

Sidekiq::Testing.inline! do
  b = Sidekiq::Batch.new
  b.on(:success, SomeCallback)
  b.jobs do
    # inline will perform jobs immediately
    5.times { HardWorker.perform_async }
  end
  # fire callback manually
  SomeCallback.new.on_success(nil, nil)
end

RSpec

See the rspec-sidekiq gem.

Capybara feature tests, with backend running in parallel

From https://makandracards.com/makandra/28125-perform-sidekiq-jobs-immediately-in-development

# config/environment/test.rb

# perform jobs immediately
config.active_job.queue_adapter = :sidekiq
require 'sidekiq/testing'
Sidekiq::Testing.inline!
...

Previous: FAQ Next: Sharding

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.