Skip to content

Testing

Mike Perham edited this page Jul 15, 2024 · 60 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('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.

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.

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