Skip to content

Testing

Steven Harman edited this page May 12, 2025 · 61 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!

Warning: Requiring sidekiq/testing will automatically call Sidekiq::Testing.fake!, so your jobs will not go to Redis. Don't require sidekiq/testing in any production code.

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

A testing API for queues 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/testing file 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.

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('[email protected]')
assert_equal 1, Sidekiq::Extensions::DelayedMailer.jobs.size

Testing Middleware

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.

Note that if your middleware is located at lib/sidekiq/middleware/server/awesome_middleware.rb, as described here, you'll need to require the class like chain.add Sidekiq::Middleware::Server::AwesomeMiddleware and define it like this in order for it to load properly into the testing chain:

module Sidekiq::Middleware::Server
  class AwesomeMiddleware
    # do stuff
  end
end

Defining the class like class Sidekiq::Middleware::Server::AwesomeMiddleware won't work properly in the test environment because Sidekiq::Middleware::Server isn't loaded automatically. You'll probably also have to require the middleware file explicitly.

Client Middleware

Depending on how you've configured your test harness and what initialization has been run, the Sidekiq::Client's middleware chain might not be empty. This is often fine, but can be problematic if you want to test some custom client middleware in isolation. The simplest way to do that is go get a fresh Sidekiq::Client instance, add your middleware to it, and then .push a job to it.

client = Sidekiq::Client.new(chain: Sidekiq::Middleware::Chain.new)
client.middleware.add(MyMiddleware)
client.push("class" => MyJob, "args" => ["what", "ever"])

assert_equal(1, MyJob.jobs.size)

Notice that we explicitly pass a new chain: to Sidekiq::Client.new. This is necessary to ensure we get a fresh, empty middleware chain, rather than defaulting to the Sidekiq.default_configuration.client_middleware instance, which might have other middleware in it.

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.

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

Clone this wiki locally