Sidekiq: add test helpers (#5326)

This commit is contained in:
rhymes 2020-01-02 14:39:40 +01:00 committed by Molly Struve
parent ba424854c2
commit 73188cd836
3 changed files with 128 additions and 8 deletions

View file

@ -75,6 +75,7 @@ RSpec.configure do |config|
config.include Devise::Test::IntegrationHelpers, type: :request
config.include FactoryBot::Syntax::Methods
config.include OmniauthMacros
config.include SidekiqTestHelpers
config.before do
ActiveRecord::Base.observers.disable :all # <-- Turn 'em all off!

View file

@ -0,0 +1,115 @@
# Helpers for Sidekiq tests
# modeled after <https://api.rubyonrails.org/v5.2/classes/ActiveJob/TestHelper.html>
# NOTE: contains code adapted from <https://github.com/rails/rails/blob/ac30e389ecfa0e26e3d44c1eda8488ddf63b3ecc/activejob/lib/active_job/test_helper.rb>
module SidekiqTestHelpers
# Provides a store of all the enqueued jobs
def sidekiq_enqueued_jobs(queue: nil)
return Sidekiq::Queues[queue.to_s] if queue
Sidekiq::Worker.jobs
end
# Asserts that the number of enqueued jobs matches the given number.
# see <https://api.rubyonrails.org/v5.2/classes/ActiveJob/TestHelper.html#method-i-assert_enqueued_jobs>
def sidekiq_assert_enqueued_jobs(number, only: nil, except: nil, queue: nil)
if block_given?
original_count = Utils.enqueued_jobs_size(only: only, except: except, queue: queue)
yield
new_count = Utils.enqueued_jobs_size(only: only, except: except, queue: queue)
error_message = "#{number} jobs expected, but #{new_count - original_count} were enqueued"
expect(new_count - original_count).to eq(number), error_message
else
actual_count = Utils.enqueued_jobs_size(only: only, except: except, queue: queue)
expect(actual_count).to eq(number), "#{number} jobs expected, but #{actual_count} were enqueued"
end
end
# Asserts that the job passed in the block has been enqueued with the given arguments.
# see <https://api.rubyonrails.org/v5.2/classes/ActiveJob/TestHelper.html#method-i-assert_enqueued_with>
def sidekiq_assert_enqueued_with(job: nil, args: nil, at: nil, queue: nil)
expected = { job: job, args: args, at: at, queue: queue }.compact
expected_args = Utils.prepare_args(expected)
yield
# check there's at least one job with the given args
matching_job = job.jobs.detect do |queued_job|
expected_args.all? { |key, value| value == queued_job[key] }
end
expect(matching_job).to be_present, "No enqueued job found with #{expected}"
end
# Asserts that no jobs have been enqueued.
# see <https://api.rubyonrails.org/v5.2/classes/ActiveJob/TestHelper.html#method-i-assert_no_enqueued_jobs>
def sidekiq_assert_no_enqueued_jobs(only: nil, except: nil, &block)
sidekiq_assert_enqueued_jobs(0, only: only, except: except, &block)
end
# Performs all enqueued jobs.
# If a block is given, performs all of the jobs that were enqueued throughout the duration of the block.
# If a block is not given, performs all of the enqueued jobs up to this point in the test.
# see <https://api.rubyonrails.org/classes/ActiveJob/TestHelper.html#method-i-perform_enqueued_jobs>
def sidekiq_perform_enqueued_jobs(only: nil, except: nil)
Utils.validate_option(only: only, except: except)
if block_given?
jobs_enqueued_before_block = sidekiq_enqueued_jobs
yield
jobs_to_perform = sidekiq_enqueued_jobs - jobs_enqueued_before_block
Utils.drain_jobs(jobs_to_perform, only: only, except: except)
else
Utils.drain_jobs(sidekiq_enqueued_jobs, only: only, except: except)
end
end
class Utils
class << self
def drain_jobs(jobs, only: nil, except: nil)
jobs_to_perform = jobs
jobs_to_perform = jobs_to_perform.filter { |j| j["class"] == only.to_s } if only
jobs_to_perform = jobs_to_perform.reject { |j| j["class"] == except.to_s } if except
jobs_to_perform.each do |job|
Sidekiq::Queues.delete_for(job["jid"], job["queue"], job["class"])
job["class"].constantize.process_job(job)
end
end
def enqueued_jobs_size(only: nil, except: nil, queue: nil)
validate_option(only: only, except: except)
Sidekiq::Worker.jobs.count do |job|
job_class = job.fetch("class")
if only
next false unless Array(only).include?(job_class.constantize)
elsif except
next false if Array(except).include?(job_class.constantize)
end
if queue
next false unless queue.to_s == job.fetch("queue")
end
true
end
end
def validate_option(only: nil, except: nil)
raise ArgumentError, "Cannot specify both `:only` and `:except` options." if only && except
end
def prepare_args(args)
args.dup.tap do |arguments|
arguments[:at] = arguments[:at].to_f if arguments[:at]
arguments[:class] = arguments[:job].to_s
arguments.delete(:job)
end.with_indifferent_access
end
end
end
end

View file

@ -1,6 +1,14 @@
require "rails_helper"
RSpec.describe RateLimitCheckerWorker, type: :worker do
describe "#perform_async" do
it "enqueues a job correctly" do
sidekiq_assert_enqueued_with(job: described_class, args: [1, "test"], queue: "default") do
described_class.perform_async(1, "test")
end
end
end
describe "#perform" do
let(:user) { create(:user) }
let(:service) { PingAdmins }
@ -9,19 +17,15 @@ RSpec.describe RateLimitCheckerWorker, type: :worker do
before { allow(service).to receive(:call) }
it "calls a service" do
perform_enqueued_jobs do
worker.perform(user.id, "test")
worker.perform(user.id, "test")
expect(service).to have_received(:call).with(user, "test").once
end
expect(service).to have_received(:call).with(user, "test").once
end
it "does nothing for non-existent user" do
perform_enqueued_jobs do
worker.perform(nil, "test")
worker.perform(nil, "test")
expect(service).not_to have_received(:call)
end
expect(service).not_to have_received(:call)
end
end
end