1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
require 'securerandom'
require 'sidekiq'
module Sidekiq
class Testing
class << self
attr_accessor :__test_mode
def __set_test_mode(mode, &block)
if block
current_mode = self.__test_mode
begin
self.__test_mode = mode
block.call
ensure
self.__test_mode = current_mode
end
else
self.__test_mode = mode
end
end
def disable!(&block)
__set_test_mode(:disable, &block)
end
def fake!(&block)
__set_test_mode(:fake, &block)
end
def inline!(&block)
__set_test_mode(:inline, &block)
end
def enabled?
self.__test_mode != :disable
end
def disabled?
self.__test_mode == :disable
end
def fake?
self.__test_mode == :fake
end
def inline?
self.__test_mode == :inline
end
end
end
# Default to fake testing to keep old behavior
Sidekiq::Testing.fake!
class EmptyQueueError < RuntimeError; end
class Client
alias_method :raw_push_real, :raw_push
def raw_push(payloads)
if Sidekiq::Testing.fake?
payloads.each do |job|
job['class'].constantize.jobs << Sidekiq.load_json(Sidekiq.dump_json(job))
end
true
elsif Sidekiq::Testing.inline?
payloads.each do |item|
jid = item['jid'] || SecureRandom.hex(12)
marshalled = Sidekiq.load_json(Sidekiq.dump_json(item))
worker = marshalled['class'].constantize.new
worker.jid = jid
worker.perform(*marshalled['args'])
end
true
else
raw_push_real(payloads)
end
end
end
module Worker
##
# The Sidekiq testing infrastructure overrides perform_async
# so that it does not actually touch the network. Instead it
# stores the asynchronous jobs in a per-class array so that
# their presence/absence can be asserted by your tests.
#
# This is similar to ActionMailer's :test delivery_method and its
# ActionMailer::Base.deliveries array.
#
# Example:
#
# require 'sidekiq/testing'
#
# assert_equal 0, HardWorker.jobs.size
# HardWorker.perform_async(:something)
# assert_equal 1, HardWorker.jobs.size
# assert_equal :something, HardWorker.jobs[0]['args'][0]
#
# assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
# MyMailer.delay.send_welcome_email('foo@example.com')
# assert_equal 1, Sidekiq::Extensions::DelayedMailer.jobs.size
#
# You can also clear and drain all workers' jobs:
#
# assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
# assert_equal 0, Sidekiq::Extensions::DelayedModel.jobs.size
#
# MyMailer.delay.send_welcome_email('foo@example.com')
# MyModel.delay.do_something_hard
#
# assert_equal 1, Sidekiq::Extensions::DelayedMailer.jobs.size
# assert_equal 1, Sidekiq::Extensions::DelayedModel.jobs.size
#
# Sidekiq::Worker.clear_all # or .drain_all
#
# assert_equal 0, Sidekiq::Extensions::DelayedMailer.jobs.size
# assert_equal 0, Sidekiq::Extensions::DelayedModel.jobs.size
#
# 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
#
# or for acceptance testing, i.e. with cucumber:
#
# AfterStep do
# Sidekiq::Worker.drain_all
# end
#
# When I sign up as "foo@example.com"
# Then I should receive a welcome email to "foo@example.com"
#
module ClassMethods
# Jobs queued for this worker
def jobs
Worker.jobs[self]
end
# Clear all jobs for this worker
def clear
jobs.clear
end
# Drain and run all jobs for this worker
def drain
while job = jobs.shift do
worker = new
worker.jid = job['jid']
execute_job(worker, job['args'])
end
end
# Pop out a single job and perform it
def perform_one
raise(EmptyQueueError, "perform_one called with empty job queue") if jobs.empty?
job = jobs.shift
worker = new
worker.jid = job['jid']
execute_job(worker, job['args'])
end
def execute_job(worker, args)
worker.perform(*args)
end
end
class << self
def jobs # :nodoc:
@jobs ||= Hash.new { |hash, key| hash[key] = [] }
end
# Clear all queued jobs across all workers
def clear_all
jobs.clear
end
# Drain all queued jobs across all workers
def drain_all
until jobs.values.all?(&:empty?) do
jobs.keys.each(&:drain)
end
end
end
end
end
|